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.Collections;
020 import java.util.HashMap;
021 import java.util.Map;
022 import java.util.Hashtable;
023 import java.util.concurrent.atomic.AtomicReference;
024 import java.util.concurrent.locks.Lock;
025 import java.util.concurrent.locks.ReentrantLock;
026
027 import javax.naming.Context;
028 import javax.naming.ContextNotEmptyException;
029 import javax.naming.NameAlreadyBoundException;
030 import javax.naming.NamingException;
031 import javax.naming.Referenceable;
032 import javax.naming.Reference;
033 import javax.naming.spi.NamingManager;
034
035 import org.apache.xbean.naming.reference.CachingReference;
036
037 /**
038 * @version $Rev$ $Date$
039 */
040 public class WritableContext extends AbstractFederatedContext {
041 private final Lock writeLock = new ReentrantLock();
042 private final AtomicReference<Map<String, Object>> bindingsRef;
043 private final AtomicReference<Map<String, Object>> indexRef;
044 private final boolean cacheReferences;
045 private final boolean supportReferenceable;
046 private final boolean checkDereferenceDifferent;
047 private final boolean assumeDereferenceBound;
048
049 public WritableContext() throws NamingException {
050 this("", Collections.<String, Object>emptyMap(), ContextAccess.MODIFIABLE, false);
051 }
052
053 public WritableContext(String nameInNamespace) throws NamingException {
054 this(nameInNamespace, Collections.<String, Object>emptyMap(), ContextAccess.MODIFIABLE, false);
055 }
056
057 public WritableContext(String nameInNamespace, Map<String, Object> bindings) throws NamingException {
058 this(nameInNamespace, bindings, ContextAccess.MODIFIABLE, false);
059 }
060
061 public WritableContext(String nameInNamespace, Map<String, Object> bindings, boolean cacheReferences) throws NamingException {
062 this(nameInNamespace, bindings, ContextAccess.MODIFIABLE, cacheReferences);
063 }
064
065 public WritableContext(String nameInNamespace, Map<String, Object> bindings, ContextAccess contextAccess) throws NamingException {
066 this(nameInNamespace, bindings, contextAccess, false);
067 }
068
069 public WritableContext(String nameInNamespace, Map<String, Object> bindings, ContextAccess contextAccess, boolean cacheReferences) throws NamingException {
070 this(nameInNamespace, bindings, contextAccess, cacheReferences, true, true, false);
071 }
072 public WritableContext(String nameInNamespace,
073 Map<String, Object> bindings,
074 ContextAccess contextAccess,
075 boolean cacheReferences,
076 boolean supportReferenceable,
077 boolean checkDereferenceDifferent,
078 boolean assumeDereferenceBound) throws NamingException {
079 super(nameInNamespace, contextAccess);
080
081 this.cacheReferences = cacheReferences;
082 if (this.cacheReferences) {
083 bindings = CachingReference.wrapReferences(bindings, this);
084 }
085 this.supportReferenceable = supportReferenceable;
086 this.checkDereferenceDifferent = checkDereferenceDifferent;
087 this.assumeDereferenceBound = assumeDereferenceBound;
088
089 Map<String, Object> localBindings = ContextUtil.createBindings(bindings, this);
090
091 this.bindingsRef = new AtomicReference<Map<String, Object>>(Collections.unmodifiableMap(localBindings));
092 this.indexRef = new AtomicReference<Map<String, Object>>(Collections.unmodifiableMap(buildIndex("", localBindings)));
093 }
094
095 protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException {
096 if (super.addBinding(name, value, rebind)) {
097 return true;
098 }
099
100 addBinding(bindingsRef, name, getNameInNamespace(name), value, rebind);
101 return true;
102 }
103
104 protected void addBinding(AtomicReference<Map<String, Object>> bindingsRef, String name, String nameInNamespace, Object value, boolean rebind) throws NamingException {
105 if (supportReferenceable && value instanceof Referenceable) {
106 Reference ref = ((Referenceable)value).getReference();
107 if (ref != null) {
108 if (checkDereferenceDifferent) {
109 try {
110 Object o = NamingManager.getObjectInstance(ref, null, null, new Hashtable());
111 if (!value.equals(o)) {
112 value = ref;
113 }
114 } catch (Exception e) {
115 if (!assumeDereferenceBound) {
116 value = ref;
117 }
118 }
119 } else {
120 value = ref;
121 }
122 }
123
124 }
125 if (cacheReferences) {
126 value = CachingReference.wrapReference(name, value, this);
127 }
128
129 writeLock.lock();
130 try {
131 Map<String, Object> bindings = bindingsRef.get();
132
133 if (!rebind && bindings.containsKey(name)) {
134 throw new NameAlreadyBoundException(name);
135 }
136 Map<String, Object> newBindings = new HashMap<String, Object>(bindings);
137 newBindings.put(name,value);
138 bindingsRef.set(newBindings);
139
140 addToIndex(nameInNamespace, value);
141 } finally {
142 writeLock.unlock();
143 }
144 }
145
146 private void addToIndex(String name, Object value) {
147 Map<String, Object> index = indexRef.get();
148 Map<String, Object> newIndex = new HashMap<String, Object>(index);
149 newIndex.put(name, value);
150 if (value instanceof NestedWritableContext) {
151 NestedWritableContext nestedcontext = (NestedWritableContext) value;
152 Map<String, Object> newIndexValues = buildIndex(name, nestedcontext.bindingsRef.get());
153 newIndex.putAll(newIndexValues);
154 }
155 indexRef.set(newIndex);
156 }
157
158 protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException {
159 if (super.removeBinding(name, removeNotEmptyContext)) {
160 return true;
161 }
162 removeBinding(bindingsRef, name, getNameInNamespace(name), removeNotEmptyContext);
163 return true;
164 }
165
166 private boolean removeBinding(AtomicReference<Map<String, Object>> bindingsRef, String name, String nameInNamespace, boolean removeNotEmptyContext) throws NamingException {
167 writeLock.lock();
168 try {
169 Map<String, Object> bindings = bindingsRef.get();
170 if (!bindings.containsKey(name)) {
171 // remove is idempotent meaning remove succeededs even if there was no value bound
172 return false;
173 }
174
175 Map<String, Object> newBindings = new HashMap<String, Object>(bindings);
176 Object oldValue = newBindings.remove(name);
177 if (!removeNotEmptyContext && oldValue instanceof Context && !isEmpty((Context)oldValue)) {
178 throw new ContextNotEmptyException(name);
179 }
180 bindingsRef.set(newBindings);
181
182 Map<String, Object> newIndex = removeFromIndex(nameInNamespace);
183 indexRef.set(newIndex);
184 return true;
185 } finally {
186 writeLock.unlock();
187 }
188 }
189
190 private Map<String, Object> removeFromIndex(String name) {
191 Map<String, Object> index = indexRef.get();
192 Map<String, Object> newIndex = new HashMap<String, Object>(index);
193 Object oldValue = newIndex.remove(name);
194 if (oldValue instanceof NestedWritableContext) {
195 NestedWritableContext nestedcontext = (NestedWritableContext) oldValue;
196 Map<String, Object> removedIndexValues = buildIndex(name, nestedcontext.bindingsRef.get());
197 for (String key : removedIndexValues.keySet()) {
198 newIndex.remove(key);
199 }
200 }
201 return newIndex;
202 }
203
204 public Context createNestedSubcontext(String path, Map<String, Object> bindings) throws NamingException {
205 if (getNameInNamespace().length() > 0) {
206 path = getNameInNamespace() + "/" + path;
207 }
208 return new NestedWritableContext(path, bindings);
209 }
210
211 private static Map<String, Object> buildIndex(String nameInNamespace, Map<String, Object> bindings) {
212 String path = nameInNamespace;
213 if (path.length() > 0 && !path.endsWith("/")) {
214 path += "/";
215 }
216
217 Map<String, Object> absoluteIndex = new HashMap<String, Object>();
218 for (Map.Entry<String, Object> entry : bindings.entrySet()) {
219 String name = entry.getKey();
220 Object value = entry.getValue();
221 if (value instanceof NestedWritableContext) {
222 NestedWritableContext nestedContext = (NestedWritableContext) value;
223 absoluteIndex.putAll(buildIndex(nestedContext.pathWithSlash, nestedContext.bindingsRef.get()));
224 }
225 absoluteIndex.put(path + name, value);
226 }
227 return absoluteIndex;
228 }
229
230 protected Object getDeepBinding(String name) {
231 Map<String, Object> index = indexRef.get();
232 return index.get(name);
233 }
234
235 protected Map<String, Object> getWrapperBindings() throws NamingException {
236 return bindingsRef.get();
237 }
238
239 /**
240 * Nested context which shares the absolute index map in MapContext.
241 */
242 public class NestedWritableContext extends AbstractFederatedContext {
243 private final AtomicReference<Map<String, Object>> bindingsRef;
244 private final String pathWithSlash;
245
246 public NestedWritableContext(String path, Map<String, Object> bindings) throws NamingException {
247 super(WritableContext.this, path);
248
249 path = getNameInNamespace();
250 if (!path.endsWith("/")) path += "/";
251 this.pathWithSlash = path;
252
253 this.bindingsRef = new AtomicReference<Map<String, Object>>(Collections.unmodifiableMap(bindings));
254 }
255
256 public Context createNestedSubcontext(String path, Map<String, Object> bindings) throws NamingException {
257 return new NestedWritableContext(getNameInNamespace(path), bindings);
258 }
259
260 protected Object getDeepBinding(String name) {
261 String absoluteName = pathWithSlash + name;
262 return WritableContext.this.getDeepBinding(absoluteName);
263 }
264
265 protected Map<String, Object> getWrapperBindings() throws NamingException {
266 return bindingsRef.get();
267 }
268
269 protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException {
270 if (super.addBinding(name, value, rebind)) {
271 return true;
272 }
273
274 WritableContext.this.addBinding(bindingsRef, name, getNameInNamespace(name), value, rebind);
275 return true;
276 }
277
278 protected boolean removeBinding(String name, boolean removeNotEmptyContext) throws NamingException {
279 if (WritableContext.this.removeBinding(bindingsRef, name, getNameInNamespace(name), removeNotEmptyContext)) {
280 return true;
281 }
282 return super.removeBinding(name, false);
283 }
284 }
285 }