View Javadoc
1   package ejava.util.jndi;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   import java.util.Properties;
6   
7   import javax.naming.Context;
8   import javax.naming.InitialContext;
9   import javax.naming.NameClassPair;
10  import javax.naming.NamingEnumeration;
11  import javax.naming.NamingException;
12  
13  import org.slf4j.Logger;
14  import org.slf4j.LoggerFactory;
15  
16  /**
17   * This class helps work with the JNDI tree.
18   */
19  public class JNDIUtil {
20  	private static final Logger logger = LoggerFactory.getLogger(JNDIUtil.class);
21  	public static final String PROPERTY_FILE="ejava-jndi.properties";
22  	private static final String[] PATHS = new String[]{
23  		"/" + PROPERTY_FILE, PROPERTY_FILE
24  	};
25  	
26  	public static InitialContext getInitialContext(String propertiesPath) throws IOException, NamingException {
27          InputStream is = null;
28          InitialContext jndi = null;
29          try {
30              //manually load the JNDI properties to make sure we don't get a Jetty JNDI tree in dev
31              if ((is=Thread.currentThread().getContextClassLoader().getResourceAsStream(propertiesPath))==null) {
32                  logger.warn("no {} found, check classpath", propertiesPath);
33              } else {
34                  Properties jndiProperties = new Properties();
35                  jndiProperties.load(is);
36                  logger.info("jndiProperties={}", jndiProperties);
37      
38                  jndi = new InitialContext(jndiProperties);
39              }
40            } finally {
41              if (is!=null) {
42                  try { is.close(); } catch(Exception ex) {}
43              }
44            }
45          return jndi;
46  	}
47  	
48  	/**
49  	 * This method will return a jndi.properties object that is based on the
50  	 * properties found in ejava-jndi.properties that start with the provided
51  	 * prefix. This method is useful when examples are using different JNDI
52  	 * mechanisms and want to keep them separate by forming the jndi.properties
53  	 * in memory.
54  	 * @param prefix
55  	 * @return Properties object that can be used during a new InitialContext(env)
56  	 * @throws IOException
57  	 */
58      public static Properties getJNDIProperties(String prefix) throws IOException {
59      	InputStream is = null;
60      	for (int i=0; is==null && i<PATHS.length; i++) {
61      		logger.debug("trying: {}", PATHS[i]);
62      		is = JNDIUtil.class.getResourceAsStream(PATHS[i]);
63      	}
64      	for (int i=0; is==null && i<PATHS.length; i++) {
65      		logger.debug("trying loader for thread: {}", PATHS[i]);
66      		is = Thread.currentThread().getContextClassLoader().getResourceAsStream(PATHS[i]);
67      	}
68      	
69      	Properties env = new Properties();
70      	if (is!=null) {
71          	Properties props = new Properties();
72          	props.load(is);
73          	is.close();
74          	
75          	for (String key : props.stringPropertyNames()) {
76          		String value = props.getProperty(key);
77          		if (key.startsWith(prefix) && value != null && !value.isEmpty()) {
78          			String name=key.substring(prefix.length(),key.length());
79          			env.put(name, value);
80          		}
81          	}
82      	} else {
83      		logger.warn("unable to locate ejava-jndi.properties from classpath");
84      	}
85      	return env;
86      }
87  	
88      /**
89       * Performs a JNDI lookup and will wait supplied number of seconds before 
90       * giving up.
91       * @param ctx
92       * @param type
93       * @param name
94       * @param waitSecs
95       * @return Object resolved from the lookup
96       * @throws NamingException 
97       */
98      @SuppressWarnings("unchecked")
99  	public static <T> T lookup(Context ctx, Class<T> type, String name, int waitSecs) 
100 			throws NamingException {
101     	logger.debug("looking up {}, wait={}", name, waitSecs);
102     	
103     	T object=null;
104     	//wait increments should be at least 1sec
105     	long interval=Math.max(waitSecs*1000/10, 1000);
106     	for (int elapsed=0; elapsed<(waitSecs*1000); elapsed += interval) {
107     		if (elapsed + interval < waitSecs*1000) {
108 	    		try {
109 					object = (T) ctx.lookup(name);
110 				} catch (Throwable ex) {
111 					logger.debug("error in jndi.lookup({})={}", name, ex);
112 					try { Thread.sleep(interval); } catch (Exception ex2) {}
113 				}
114     		} else {
115 				object = (T) ctx.lookup(name);
116     		}
117     	}
118     	logger.debug("object=" + object);
119     	return object;
120     }
121 		
122 	/**
123      * Produces a debug string listing the JNDI contents of the current, default 
124      * Context.
125      * @return String describing contents of context
126 	 * @throws NamingException
127 	 */
128     public String dump() throws NamingException {
129         return dump(new InitialContext(),"");
130     }
131 
132     /**
133      * Produces a debug string listing the JNDI contents of the specified 
134      * Context.
135      * @param context
136      * @param name
137      * @return String describing contents of context
138      */
139     public String dump(Context context, String name) {
140         StringBuilder text = new StringBuilder();
141         if (name==null) { name = ""; }
142         try {
143             text.append("listing ").append(name);
144             doDump(0, text, context, name);
145         }
146         catch (NamingException ex) {}
147         return text.toString();
148     }
149 
150 	private void doDump(int level, StringBuilder text, Context context, String name) 
151         throws NamingException {
152         for (NamingEnumeration<NameClassPair> ne = context.list(name); ne.hasMore();) {
153             NameClassPair ncp = (NameClassPair) ne.next();
154             String objectName = ncp.getName();
155             String className = ncp.getClassName();
156             if (isContext(className)) {
157             	text.append(getPad(level))
158             	    .append("+")
159             	    .append(objectName)
160             	    .append(":")
161             	    .append(className)
162             	    .append('\n');
163                 doDump(level + 1, text, context, name + "/" + objectName);
164             } else {
165             	text.append(getPad(level))
166 	        	    .append("-")
167 	        	    .append(objectName)
168 	        	    .append(":")
169 	        	    .append(className)
170 	        	    .append('\n');
171             }
172         }
173     }
174     
175 	protected boolean isContext(String className) {
176         try {
177 			Class<?> objectClass = Thread.currentThread().getContextClassLoader()
178                     .loadClass(className);
179             return Context.class.isAssignableFrom(objectClass);
180         }
181         catch (ClassNotFoundException ex) {
182             //object is probably not a context, report as non-context
183             return false;
184         }
185     }
186 
187     protected String getPad(int level) {
188         StringBuilder pad = new StringBuilder();
189         for (int i = 0; i < level; i++) {
190             pad.append(" ");
191         }
192         return pad.toString();
193     }
194 }