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 }