001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.xbean.naming.context; 018 019 import java.util.concurrent.atomic.AtomicReference; 020 import java.util.concurrent.locks.Lock; 021 import java.util.concurrent.locks.ReentrantLock; 022 import org.apache.xbean.naming.reference.CachingReference; 023 024 import javax.naming.Context; 025 import javax.naming.NamingException; 026 import javax.naming.NameAlreadyBoundException; 027 import javax.naming.ContextNotEmptyException; 028 import java.util.Collections; 029 import java.util.HashMap; 030 import java.util.Iterator; 031 import java.util.Map; 032 033 /** 034 * @version $Rev$ $Date$ 035 */ 036 public class WritableContext extends AbstractFederatedContext { 037 private final Lock writeLock = new ReentrantLock(); 038 private final AtomicReference bindingsRef; 039 private final AtomicReference indexRef; 040 private final boolean cacheReferences; 041 042 public WritableContext() throws NamingException { 043 this("", Collections.EMPTY_MAP, ContextAccess.MODIFIABLE, false); 044 } 045 046 public WritableContext(String nameInNamespace) throws NamingException { 047 this(nameInNamespace, Collections.EMPTY_MAP, ContextAccess.MODIFIABLE, false); 048 } 049 050 public WritableContext(String nameInNamespace, Map bindings) throws NamingException { 051 this(nameInNamespace, bindings, ContextAccess.MODIFIABLE, false); 052 } 053 054 public WritableContext(String nameInNamespace, Map bindings, boolean cacheReferences) throws NamingException { 055 this(nameInNamespace, bindings, ContextAccess.MODIFIABLE, cacheReferences); 056 } 057 058 public WritableContext(String nameInNamespace, Map bindings, ContextAccess contextAccess) throws NamingException { 059 this(nameInNamespace, bindings, contextAccess, false); 060 } 061 062 public WritableContext(String nameInNamespace, Map bindings, ContextAccess contextAccess, boolean cacheReferences) throws NamingException { 063 super(nameInNamespace, contextAccess); 064 065 this.cacheReferences = cacheReferences; 066 if (this.cacheReferences) { 067 bindings = CachingReference.wrapReferences(bindings); 068 } 069 070 Map localBindings = ContextUtil.createBindings(bindings, this); 071 072 this.bindingsRef = new AtomicReference(Collections.unmodifiableMap(localBindings)); 073 this.indexRef = new AtomicReference(Collections.unmodifiableMap(buildIndex("", localBindings))); 074 } 075 076 protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException { 077 if (super.addBinding(name, value, rebind)) { 078 return true; 079 } 080 081 addBinding(bindingsRef, name, getNameInNamespace(name), value, rebind); 082 return true; 083 } 084 085 protected void addBinding(AtomicReference bindingsRef, String name, String nameInNamespace, Object value, boolean rebind) throws NamingException { 086 writeLock.lock(); 087 try { 088 Map bindings = (Map) bindingsRef.get(); 089 090 if (!rebind && bindings.containsKey(name)) { 091 throw new NameAlreadyBoundException(name); 092 } 093 if (cacheReferences) { 094 value = CachingReference.wrapReference(getNameInNamespace(name), value); 095 } 096 097 Map newBindings = new HashMap(bindings); 098 newBindings.put(name,value); 099 bindingsRef.set(newBindings); 100 101 addToIndex(nameInNamespace, value); 102 } finally { 103 writeLock.unlock(); 104 } 105 } 106 107 private void addToIndex(String name, Object value) { 108 Map index = (Map) indexRef.get(); 109 Map newIndex = new HashMap(index); 110 newIndex.put(name, value); 111 if (value instanceof NestedWritableContext) { 112 NestedWritableContext nestedcontext = (NestedWritableContext) value; 113 Map newIndexValues = buildIndex(name, (Map) nestedcontext.bindingsRef.get()); 114 newIndex.putAll(newIndexValues); 115 } 116 indexRef.set(newIndex); 117 } 118 119 protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { 120 if (super.removeBinding(name, removeNotEmptyContext)) { 121 return true; 122 } 123 removeBinding(bindingsRef, name, removeNotEmptyContext); 124 return true; 125 } 126 127 private boolean removeBinding(AtomicReference bindingsRef, String name, boolean removeNotEmptyContext) throws NamingException { 128 writeLock.lock(); 129 try { 130 Map bindings = (Map) bindingsRef.get(); 131 if (!bindings.containsKey(name)) { 132 // remove is idempotent meaning remove succeededs even if there was no value bound 133 return false; 134 } 135 136 Map newBindings = new HashMap(bindings); 137 Object oldValue = newBindings.remove(name); 138 if (!removeNotEmptyContext && oldValue instanceof Context && !isEmpty((Context)oldValue)) { 139 throw new ContextNotEmptyException(name); 140 } 141 bindingsRef.set(newBindings); 142 143 Map newIndex = removeFromIndex(name); 144 indexRef.set(newIndex); 145 return true; 146 } finally { 147 writeLock.unlock(); 148 } 149 } 150 151 private Map removeFromIndex(String name) { 152 Map index = (Map) indexRef.get(); 153 Map newIndex = new HashMap(index); 154 Object oldValue = newIndex.remove(name); 155 if (oldValue instanceof NestedWritableContext) { 156 NestedWritableContext nestedcontext = (NestedWritableContext) oldValue; 157 Map removedIndexValues = buildIndex(name, (Map) nestedcontext.bindingsRef.get()); 158 for (Iterator iterator = removedIndexValues.keySet().iterator(); iterator.hasNext();) { 159 String key = (String) iterator.next(); 160 newIndex.remove(key); 161 } 162 } 163 return newIndex; 164 } 165 166 public Context createNestedSubcontext(String path, Map bindings) throws NamingException { 167 return new NestedWritableContext(path,bindings); 168 } 169 170 private static Map buildIndex(String nameInNamespace, Map bindings) { 171 String path = nameInNamespace; 172 if (path.length() > 0 && !path.endsWith("/")) { 173 path += "/"; 174 } 175 176 Map absoluteIndex = new HashMap(); 177 for (Iterator iterator = bindings.entrySet().iterator(); iterator.hasNext();) { 178 Map.Entry entry = (Map.Entry) iterator.next(); 179 String name = (String) entry.getKey(); 180 Object value = entry.getValue(); 181 if (value instanceof NestedWritableContext) { 182 NestedWritableContext nestedContext = (NestedWritableContext)value; 183 absoluteIndex.putAll(buildIndex(nestedContext.pathWithSlash, (Map) nestedContext.bindingsRef.get())); 184 } 185 absoluteIndex.put(path + name, value); 186 } 187 return absoluteIndex; 188 } 189 190 protected Object getDeepBinding(String name) { 191 Map index = (Map) indexRef.get(); 192 return index.get(name); 193 } 194 195 protected Map getWrapperBindings() throws NamingException { 196 Map bindings = (Map) bindingsRef.get(); 197 return bindings; 198 } 199 200 /** 201 * Nested context which shares the absolute index map in MapContext. 202 */ 203 public class NestedWritableContext extends AbstractFederatedContext { 204 private final AtomicReference bindingsRef; 205 private final String pathWithSlash; 206 207 public NestedWritableContext(String path, Map bindings) throws NamingException { 208 super(WritableContext.this, path); 209 210 if (!path.endsWith("/")) path += "/"; 211 this.pathWithSlash = path; 212 213 this.bindingsRef = new AtomicReference(Collections.unmodifiableMap(bindings)); 214 } 215 216 public Context createNestedSubcontext(String path, Map bindings) throws NamingException { 217 return new NestedWritableContext(path, bindings); 218 } 219 220 protected Object getDeepBinding(String name) { 221 String absoluteName = pathWithSlash + name; 222 return WritableContext.this.getDeepBinding(absoluteName); 223 } 224 225 protected Map getWrapperBindings() throws NamingException { 226 Map bindings = (Map) bindingsRef.get(); 227 return bindings; 228 } 229 230 protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException { 231 if (super.addBinding(name, value, rebind)) { 232 return true; 233 } 234 235 WritableContext.this.addBinding(bindingsRef, name, getNameInNamespace(name), value, rebind); 236 return true; 237 } 238 239 protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException { 240 if (WritableContext.this.removeBinding(bindingsRef, name, false)) { 241 return true; 242 } 243 return super.removeBinding(name, false); 244 } 245 } 246 }