001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.mock.jndi;
018:
019: import java.util.HashMap;
020: import java.util.Hashtable;
021: import java.util.Iterator;
022: import java.util.Map;
023:
024: import javax.naming.Binding;
025: import javax.naming.Context;
026: import javax.naming.Name;
027: import javax.naming.NameClassPair;
028: import javax.naming.NameNotFoundException;
029: import javax.naming.NameParser;
030: import javax.naming.NamingEnumeration;
031: import javax.naming.NamingException;
032: import javax.naming.OperationNotSupportedException;
033:
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036:
037: import org.springframework.util.StringUtils;
038:
039: /**
040: * Simple implementation of a JNDI naming context.
041: * Only supports binding plain Objects to String names.
042: * Mainly for test environments, but also usable for standalone applications.
043: *
044: * <p>This class is not intended for direct usage by applications, although it
045: * can be used for example to override JndiTemplate's <code>createInitialContext</code>
046: * method in unit tests. Typically, SimpleNamingContextBuilder will be used to
047: * set up a JVM-level JNDI environment.
048: *
049: * @author Rod Johnson
050: * @author Juergen Hoeller
051: * @see org.springframework.mock.jndi.SimpleNamingContextBuilder
052: * @see org.springframework.jndi.JndiTemplate#createInitialContext
053: */
054: public class SimpleNamingContext implements Context {
055:
056: private final Log logger = LogFactory.getLog(getClass());
057:
058: private final String root;
059:
060: private final Hashtable boundObjects;
061:
062: private final Hashtable environment = new Hashtable();
063:
064: /**
065: * Create a new naming context.
066: */
067: public SimpleNamingContext() {
068: this ("");
069: }
070:
071: /**
072: * Create a new naming context with the given naming root.
073: */
074: public SimpleNamingContext(String root) {
075: this .root = root;
076: this .boundObjects = new Hashtable();
077: }
078:
079: /**
080: * Create a new naming context with the given naming root,
081: * the given name/object map, and the JNDI environment entries.
082: */
083: public SimpleNamingContext(String root, Hashtable boundObjects,
084: Hashtable environment) {
085: this .root = root;
086: this .boundObjects = boundObjects;
087: if (environment != null) {
088: this .environment.putAll(environment);
089: }
090: }
091:
092: // Actual implementations of Context methods follow
093:
094: public NamingEnumeration list(String root) throws NamingException {
095: if (logger.isDebugEnabled()) {
096: logger.debug("Listing name/class pairs under [" + root
097: + "]");
098: }
099: return new NameClassPairEnumeration(this , root);
100: }
101:
102: public NamingEnumeration listBindings(String root)
103: throws NamingException {
104: if (logger.isDebugEnabled()) {
105: logger.debug("Listing bindings under [" + root + "]");
106: }
107: return new BindingEnumeration(this , root);
108: }
109:
110: /**
111: * Look up the object with the given name.
112: * Note: Not intended for direct use by applications.
113: * Will be used by any standard InitialContext JNDI lookups.
114: * @throws javax.naming.NameNotFoundException if the object could not be found
115: */
116: public Object lookup(String pname) throws NameNotFoundException {
117: String name = this .root + pname;
118: if (logger.isDebugEnabled()) {
119: logger.debug("Static JNDI lookup: [" + name + "]");
120: }
121: if ("".equals(name)) {
122: return new SimpleNamingContext(this .root,
123: this .boundObjects, this .environment);
124: }
125: Object found = this .boundObjects.get(name);
126: if (found == null) {
127: if (!name.endsWith("/")) {
128: name = name + "/";
129: }
130: for (Iterator it = this .boundObjects.keySet().iterator(); it
131: .hasNext();) {
132: String boundName = (String) it.next();
133: if (boundName.startsWith(name)) {
134: return new SimpleNamingContext(name,
135: this .boundObjects, this .environment);
136: }
137: }
138: throw new NameNotFoundException("Name ["
139: + this .root
140: + pname
141: + "] not bound; "
142: + this .boundObjects.size()
143: + " bindings: ["
144: + StringUtils.collectionToDelimitedString(
145: this .boundObjects.keySet(), ",") + "]");
146: }
147: return found;
148: }
149:
150: public Object lookupLink(String name) throws NameNotFoundException {
151: return lookup(name);
152: }
153:
154: /**
155: * Bind the given object to the given name.
156: * Note: Not intended for direct use by applications
157: * if setting up a JVM-level JNDI environment.
158: * Use SimpleNamingContextBuilder to set up JNDI bindings then.
159: * @see org.springframework.mock.jndi.SimpleNamingContextBuilder#bind
160: */
161: public void bind(String name, Object obj) {
162: if (logger.isInfoEnabled()) {
163: logger.info("Static JNDI binding: [" + this .root + name
164: + "] = [" + obj + "]");
165: }
166: this .boundObjects.put(this .root + name, obj);
167: }
168:
169: public void unbind(String name) {
170: if (logger.isInfoEnabled()) {
171: logger.info("Static JNDI remove: [" + this .root + name
172: + "]");
173: }
174: this .boundObjects.remove(this .root + name);
175: }
176:
177: public void rebind(String name, Object obj) {
178: bind(name, obj);
179: }
180:
181: public void rename(String oldName, String newName)
182: throws NameNotFoundException {
183: Object obj = lookup(oldName);
184: unbind(oldName);
185: bind(newName, obj);
186: }
187:
188: public Context createSubcontext(String name) {
189: Context subcontext = new SimpleNamingContext(this .root + name,
190: this .boundObjects, this .environment);
191: bind(name, subcontext);
192: return subcontext;
193: }
194:
195: public void destroySubcontext(String name) {
196: unbind(name);
197: }
198:
199: public String composeName(String name, String prefix) {
200: return prefix + name;
201: }
202:
203: public Hashtable getEnvironment() {
204: return environment;
205: }
206:
207: public Object addToEnvironment(String propName, Object propVal) {
208: return environment.put(propName, propVal);
209: }
210:
211: public Object removeFromEnvironment(String propName) {
212: return environment.remove(propName);
213: }
214:
215: public void close() {
216: }
217:
218: // Unsupported methods follow: no support for javax.naming.Name
219:
220: public NamingEnumeration list(Name name) throws NamingException {
221: throw new OperationNotSupportedException(
222: "SimpleNamingContext does not support [javax.naming.Name]");
223: }
224:
225: public NamingEnumeration listBindings(Name name)
226: throws NamingException {
227: throw new OperationNotSupportedException(
228: "SimpleNamingContext does not support [javax.naming.Name]");
229: }
230:
231: public Object lookup(Name name) throws NamingException {
232: throw new OperationNotSupportedException(
233: "SimpleNamingContext does not support [javax.naming.Name]");
234: }
235:
236: public Object lookupLink(Name name) throws NamingException {
237: throw new OperationNotSupportedException(
238: "SimpleNamingContext does not support [javax.naming.Name]");
239: }
240:
241: public void bind(Name name, Object obj) throws NamingException {
242: throw new OperationNotSupportedException(
243: "SimpleNamingContext does not support [javax.naming.Name]");
244: }
245:
246: public void unbind(Name name) throws NamingException {
247: throw new OperationNotSupportedException(
248: "SimpleNamingContext does not support [javax.naming.Name]");
249: }
250:
251: public void rebind(Name name, Object obj) throws NamingException {
252: throw new OperationNotSupportedException(
253: "SimpleNamingContext does not support [javax.naming.Name]");
254: }
255:
256: public void rename(Name oldName, Name newName)
257: throws NamingException {
258: throw new OperationNotSupportedException(
259: "SimpleNamingContext does not support [javax.naming.Name]");
260: }
261:
262: public Context createSubcontext(Name name) throws NamingException {
263: throw new OperationNotSupportedException(
264: "SimpleNamingContext does not support [javax.naming.Name]");
265: }
266:
267: public void destroySubcontext(Name name) throws NamingException {
268: throw new OperationNotSupportedException(
269: "SimpleNamingContext does not support [javax.naming.Name]");
270: }
271:
272: public String getNameInNamespace() throws NamingException {
273: throw new OperationNotSupportedException(
274: "SimpleNamingContext does not support [javax.naming.Name]");
275: }
276:
277: public NameParser getNameParser(Name name) throws NamingException {
278: throw new OperationNotSupportedException(
279: "SimpleNamingContext does not support [javax.naming.Name]");
280: }
281:
282: public NameParser getNameParser(String name) throws NamingException {
283: throw new OperationNotSupportedException(
284: "SimpleNamingContext does not support [javax.naming.Name]");
285: }
286:
287: public Name composeName(Name name, Name prefix)
288: throws NamingException {
289: throw new OperationNotSupportedException(
290: "SimpleNamingContext does not support [javax.naming.Name]");
291: }
292:
293: private static abstract class AbstractNamingEnumeration implements
294: NamingEnumeration {
295:
296: private Iterator iterator;
297:
298: private AbstractNamingEnumeration(SimpleNamingContext context,
299: String proot) throws NamingException {
300: if (!"".equals(proot) && !proot.endsWith("/")) {
301: proot = proot + "/";
302: }
303: String root = context.root + proot;
304: Map contents = new HashMap();
305: Iterator it = context.boundObjects.keySet().iterator();
306: while (it.hasNext()) {
307: String boundName = (String) it.next();
308: if (boundName.startsWith(root)) {
309: int startIndex = root.length();
310: int endIndex = boundName.indexOf('/', startIndex);
311: String strippedName = (endIndex != -1 ? boundName
312: .substring(startIndex, endIndex)
313: : boundName.substring(startIndex));
314: if (!contents.containsKey(strippedName)) {
315: try {
316: contents.put(strippedName, createObject(
317: strippedName, context.lookup(proot
318: + strippedName)));
319: } catch (NameNotFoundException ex) {
320: // cannot happen
321: }
322: }
323: }
324: }
325: if (contents.size() == 0) {
326: throw new NamingException("Invalid root: ["
327: + context.root + proot + "]");
328: }
329: this .iterator = contents.values().iterator();
330: }
331:
332: protected abstract Object createObject(String strippedName,
333: Object obj);
334:
335: public boolean hasMore() {
336: return iterator.hasNext();
337: }
338:
339: public Object next() {
340: return iterator.next();
341: }
342:
343: public boolean hasMoreElements() {
344: return iterator.hasNext();
345: }
346:
347: public Object nextElement() {
348: return iterator.next();
349: }
350:
351: public void close() {
352: }
353: }
354:
355: private static class NameClassPairEnumeration extends
356: AbstractNamingEnumeration {
357:
358: private NameClassPairEnumeration(SimpleNamingContext context,
359: String root) throws NamingException {
360: super (context, root);
361: }
362:
363: protected Object createObject(String strippedName, Object obj) {
364: return new NameClassPair(strippedName, obj.getClass()
365: .getName());
366: }
367: }
368:
369: private static class BindingEnumeration extends
370: AbstractNamingEnumeration {
371:
372: private BindingEnumeration(SimpleNamingContext context,
373: String root) throws NamingException {
374: super (context, root);
375: }
376:
377: protected Object createObject(String strippedName, Object obj) {
378: return new Binding(strippedName, obj);
379: }
380: }
381:
382: }
|