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 }