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:
018: package org.apache.harmony.jndi.internal;
019:
020: import java.applet.Applet;
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.net.URL;
026: import java.security.AccessController;
027: import java.security.PrivilegedAction;
028: import java.security.PrivilegedActionException;
029: import java.security.PrivilegedExceptionAction;
030: import java.util.ArrayList;
031: import java.util.Enumeration;
032: import java.util.Hashtable;
033: import java.util.List;
034: import java.util.Properties;
035: import java.util.StringTokenizer;
036:
037: import javax.naming.ConfigurationException;
038: import javax.naming.Context;
039: import javax.naming.NamingException;
040: import javax.naming.ldap.LdapContext;
041:
042: import org.apache.harmony.jndi.internal.nls.Messages;
043:
044: /**
045: * This is a utility class that reads environment properties.
046: */
047: public final class EnvironmentReader {
048:
049: // The name of application resource files
050: private static final String APPLICATION_RESOURCE_FILE = "jndi.properties"; //$NON-NLS-1$
051:
052: // The name of provider resource file
053: private static final String PROVIDER_RESOURCE_FILE = "jndiprovider.properties"; //$NON-NLS-1$
054:
055: // Not allowed to create an instance
056: private EnvironmentReader() {
057: super ();
058: }
059:
060: /*
061: * Merge additional properties with already read ones.
062: *
063: * @param src - the source containing additional properties @param dst - the
064: * destination to put additional properties @param valueAddToList - whether
065: * to add new values of C-type properties
066: */
067: public static void mergeEnvironment(final Hashtable<?, ?> src,
068: final Hashtable<Object, Object> dst,
069: final boolean valueAddToList) {
070:
071: Object key = null;
072: String val = null;
073: Enumeration<?> keys = src.keys();
074:
075: while (keys.hasMoreElements()) {
076: key = keys.nextElement();
077:
078: if (!dst.containsKey(key)) {
079: /*
080: * If this property doesn't exist yet, add it.
081: */
082: dst.put(key, src.get(key));
083: } else if (valueAddToList
084: && (LdapContext.CONTROL_FACTORIES.equals(key)
085: || Context.OBJECT_FACTORIES.equals(key)
086: || Context.STATE_FACTORIES.equals(key) || Context.URL_PKG_PREFIXES
087: .equals(key))) {
088: /*
089: * Otherwise, if this property can contain a list of values, add
090: * the additional values if the flag "valueAddToList" is true.
091: */
092:
093: // Read the original value
094: val = (String) dst.get(key);
095: // Values are combined into a single list separated by colons.
096: val = val + ":" + src.get(key); //$NON-NLS-1$
097: // The final value becomes the resulting value of that property
098: dst.put(key, val);
099: } else {
100: /*
101: * Otherwise, ignore the found value.
102: */
103: }
104: }
105: }
106:
107: /*
108: * Get the required 7 JNDI properties from JNDI properties source. This
109: * method is designed as package visibility to improve performance when
110: * called by anonymous inner classes.
111: *
112: * @return a hashtable holding the required properties.
113: */
114: static Hashtable<Object, Object> filterProperties(
115: final JNDIPropertiesSource source) {
116: final Hashtable<Object, Object> filteredProperties = new Hashtable<Object, Object>();
117: String propValue = null;
118:
119: propValue = source.getProperty(Context.INITIAL_CONTEXT_FACTORY);
120: if (null != propValue) {
121: filteredProperties.put(Context.INITIAL_CONTEXT_FACTORY,
122: propValue);
123: }
124:
125: propValue = source.getProperty(Context.DNS_URL);
126: if (null != propValue) {
127: filteredProperties.put(Context.DNS_URL, propValue);
128: }
129:
130: propValue = source.getProperty(Context.PROVIDER_URL);
131: if (null != propValue) {
132: filteredProperties.put(Context.PROVIDER_URL, propValue);
133: }
134:
135: propValue = source.getProperty(Context.OBJECT_FACTORIES);
136: if (null != propValue) {
137: filteredProperties.put(Context.OBJECT_FACTORIES, propValue);
138: }
139:
140: propValue = source.getProperty(Context.STATE_FACTORIES);
141: if (null != propValue) {
142: filteredProperties.put(Context.STATE_FACTORIES, propValue);
143: }
144:
145: propValue = source.getProperty(Context.URL_PKG_PREFIXES);
146: if (null != propValue) {
147: filteredProperties.put(Context.URL_PKG_PREFIXES, propValue);
148: }
149:
150: propValue = source.getProperty(LdapContext.CONTROL_FACTORIES);
151: if (null != propValue) {
152: filteredProperties.put(LdapContext.CONTROL_FACTORIES,
153: propValue);
154: }
155:
156: return filteredProperties;
157: }
158:
159: /*
160: * Read the required 7 JNDI properties from system properties and merge with
161: * existing properties. Note that the values of C-type properties are only
162: * included when no corresponding value is presented in existing properties.
163: *
164: * @param existingProps - existing properties
165: */
166: public static void readSystemProperties(
167: final Hashtable<Object, Object> existingProps) {
168: /*
169: * Privileged code is used to access system properties. This is required
170: * if JNDI is run in Applet or other applications which only have
171: * limited permissions to access certain resources.
172: */
173: Hashtable<Object, Object> systemProperties = AccessController
174: .doPrivileged(new PrivilegedAction<Hashtable<Object, Object>>() {
175: public Hashtable<Object, Object> run() {
176: return filterProperties(new SystemPropertiesSource());
177: }
178: });
179: mergeEnvironment(systemProperties, existingProps, false);
180: }
181:
182: /*
183: * Read the required 7 JNDI properties from applet parameters and merge with
184: * existing properties. Note that the values of C-type properties are only
185: * included when no corresponding value is presented in existing properties.
186: *
187: * @param applet - the applet object @param existingProps - existing
188: * properties
189: */
190: public static void readAppletParameters(Object applet,
191: Hashtable<Object, Object> existingProps) {
192: if (null != applet) {
193: Hashtable<Object, Object> appletParameters = filterProperties(new AppletParametersSource(
194: (Applet) applet));
195: mergeEnvironment(appletParameters, existingProps, false);
196: }
197: }
198:
199: /*
200: * Read multiple resource files from the classpaths given the file name.
201: * This method is designed as package visibility to improve performance when
202: * called by anonymous inner classes.
203: *
204: * @param name - the name of the resource file @param existingProps -
205: * existing properties, cannot be null @param filter - to filter properties
206: */
207: static Hashtable<Object, Object> readMultipleResourceFiles(
208: final String name,
209: final Hashtable<Object, Object> existingProps,
210: ClassLoader cl) throws NamingException {
211:
212: if (null == cl) {
213: cl = ClassLoader.getSystemClassLoader();
214: }
215:
216: Enumeration<URL> e = null;
217: try {
218: // Load all resource files
219: e = cl.getResources(name);
220: } catch (IOException ex) {
221: // Unexpected ClassLoader exception
222: // jndi.23=Failed to load JNDI resource files.
223: ConfigurationException newEx = new ConfigurationException(
224: Messages.getString("jndi.23")); //$NON-NLS-1$
225: newEx.setRootCause(ex);
226: throw newEx;
227: }
228:
229: // Read all the loaded properties and merge
230: URL url = null;
231: InputStream is = null;
232: final Properties p = new Properties();
233: while (e.hasMoreElements()) {
234: url = e.nextElement();
235: try {
236: if (null != (is = url.openStream())) {
237: p.load(is);
238: mergeEnvironment(p, existingProps, true);
239: p.clear();
240: }
241: } catch (IOException ex) {
242: // Can't read this resource file
243: // jndi.24=Failed to read JNDI resource files.
244: ConfigurationException newEx = new ConfigurationException(
245: Messages.getString("jndi.24")); //$NON-NLS-1$
246: newEx.setRootCause(ex);
247: throw newEx;
248: } finally {
249: try {
250: if (null != is) {
251: is.close();
252: }
253: } catch (IOException ex) {
254: // Ignore closing exception
255: } finally {
256: is = null;
257: }
258: }
259: }
260: return existingProps;
261: }
262:
263: /*
264: * Read application/applet resource files.
265: *
266: * @param existingProps - existing properties, cannot be null.
267: */
268: public static Hashtable<Object, Object> readApplicationResourceFiles(
269: final Hashtable<Object, Object> existingProps)
270: throws NamingException {
271: // Use privileged code to read the application resource files
272: try {
273: AccessController
274: .doPrivileged(new PrivilegedExceptionAction<Void>() {
275: public Void run() throws NamingException {
276: readMultipleResourceFiles(
277: APPLICATION_RESOURCE_FILE,
278: existingProps, Thread
279: .currentThread()
280: .getContextClassLoader());
281: return null;
282: }
283: });
284: } catch (PrivilegedActionException e) {
285: Exception rootCause = e.getException();
286: if (rootCause instanceof NamingException) {
287: throw (NamingException) rootCause;
288: } else if (rootCause instanceof RuntimeException) {
289: throw (RuntimeException) rootCause;
290: } else {
291: // This should not happen.
292: }
293: }
294: return existingProps;
295: }
296:
297: /*
298: * Read the properties file "java.home"/lib/jndi.properties. Pay attention
299: * to the privileged code for accessing this external resource file. This is
300: * required if JNDI is run in Applet or other applications which only have
301: * limited permissions to access certain resources.
302: *
303: * @param existingProps - existing properties, cannot be null.
304: */
305: public static Hashtable<Object, Object> readLibraryResourceFile(
306: final Hashtable<Object, Object> existingProps)
307: throws NamingException {
308: final String sep = System.getProperty("file.separator"); //$NON-NLS-1$
309:
310: String resPath = null;
311: // Construct the full filename of "java.home"/lib/jndi.properties
312: resPath = System.getProperty("java.home"); //$NON-NLS-1$
313: if (!resPath.endsWith(sep)) {
314: resPath += sep;
315: }
316: resPath += "lib" + sep + APPLICATION_RESOURCE_FILE; //$NON-NLS-1$
317:
318: // Try to read this properties if it exists
319: InputStream is = null;
320: final File resFile = new File(resPath);
321: final Properties p = new Properties();
322: // Use privileged code to determine whether the file exists
323: boolean resFileExists = AccessController.doPrivileged(
324: new PrivilegedAction<Boolean>() {
325: public Boolean run() {
326: return Boolean.valueOf(resFile.exists());
327: }
328: }).booleanValue();
329: if (resFileExists) {
330: try {
331: // Use privileged code to read the file
332: is = AccessController
333: .doPrivileged(new PrivilegedExceptionAction<FileInputStream>() {
334: public FileInputStream run()
335: throws IOException {
336: FileInputStream localInputStream = new FileInputStream(
337: resFile);
338: p.load(localInputStream);
339: return localInputStream;
340: }
341: });
342: mergeEnvironment(p, existingProps, true);
343: } catch (PrivilegedActionException e) {
344: // Can't read "java.home"/lib/jndi.properties
345: // jndi.25=Failed to read JNDI resource files in java home
346: // library.
347: ConfigurationException newEx = new ConfigurationException(
348: Messages.getString("jndi.25")); //$NON-NLS-1$
349: newEx.setRootCause(e.getException());
350: throw newEx;
351: } finally {
352: try {
353: if (null != is) {
354: is.close();
355: }
356: } catch (IOException ex) {
357: // Ignore closing exception
358: }
359: }
360: }
361: return existingProps;
362: }
363:
364: /*
365: * Read the service provider resource file.
366: *
367: * @param context - the context @param existingProps - existing properties,
368: * cannot be null.
369: */
370: public static Hashtable<Object, Object> readProviderResourceFiles(
371: final Context context,
372: final Hashtable<Object, Object> existingProps)
373: throws NamingException {
374:
375: String factory = context.getClass().getName();
376: String resPath = null;
377: int len = factory.lastIndexOf('.');
378:
379: // Construct the full filename of the service provider resource file
380: if (-1 == len) {
381: // Default package
382: resPath = PROVIDER_RESOURCE_FILE;
383: } else {
384: // Replace "." with '/'
385: resPath = factory.substring(0, len + 1);
386: resPath = resPath.replace('.', '/');
387: resPath += PROVIDER_RESOURCE_FILE;
388: }
389:
390: // Use privileged code to read the provider resource files
391: try {
392: final String finalResPath = resPath;
393: AccessController
394: .doPrivileged(new PrivilegedExceptionAction<String>() {
395: public String run() throws NamingException {
396: readMultipleResourceFiles(finalResPath,
397: existingProps, context.getClass()
398: .getClassLoader());
399: return null;
400: }
401: });
402: } catch (PrivilegedActionException e) {
403: Exception rootCause = e.getException();
404: if (rootCause instanceof NamingException) {
405: throw (NamingException) rootCause;
406: } else if (rootCause instanceof RuntimeException) {
407: throw (RuntimeException) rootCause;
408: } else {
409: // This should not happen.
410: throw new AssertionError(rootCause);
411: }
412: }
413: return existingProps;
414: }
415:
416: /*
417: * Get the list of the specified factory names from the supplied environment
418: * and the resource provider files of the given Context.
419: *
420: * @param envmt The supplied environment. @param ctx The Context whose
421: * resource provider files will be read. @param key The name of the factory.
422: * @return The list of the desired factory names. @throws NamingException If
423: * an error occurs when reading the provider resource files.
424: */
425: public static String[] getFactoryNamesFromEnvironmentAndProviderResource(
426: Hashtable<?, ?> envmt, Context ctx, String key)
427: throws NamingException {
428:
429: List<String> fnames = new ArrayList<String>();
430:
431: // collect tokens from envmt with key
432: if (null != envmt) {
433: String str = (String) envmt.get(key);
434: if (null != str) {
435: StringTokenizer st = new StringTokenizer(str, ":"); //$NON-NLS-1$
436: while (st.hasMoreTokens()) {
437: fnames.add(st.nextToken());
438: }
439: }
440: }
441: // collect tokens from ctx's provider resource file
442: if (null != ctx) {
443: Hashtable<Object, Object> h = new Hashtable<Object, Object>();
444: // read provider resource file from ctx's package
445: EnvironmentReader.readProviderResourceFiles(ctx, h);
446: String str = (String) h.get(key);
447: if (null != str) {
448: StringTokenizer st = new StringTokenizer(str, ":"); //$NON-NLS-1$
449: while (st.hasMoreTokens()) {
450: fnames.add(st.nextToken());
451: }
452: }
453: }
454: // if key is Context.URL_PKG_PREFIXES, append "com.sun.jndi.url" at the
455: // end
456: if (Context.URL_PKG_PREFIXES.equals(key)) {
457: fnames.add("com.sun.jndi.url"); //$NON-NLS-1$
458: }
459: // return factory names
460: return fnames.toArray(new String[fnames.size()]);
461: }
462:
463: /*
464: * Wrapper interface for JNDI properties source.
465: */
466: private interface JNDIPropertiesSource {
467: // Get a JNDI property with the specified name
468: String getProperty(final String propName);
469: }
470:
471: /*
472: * Wrapper class for system properties source.
473: */
474: private static class SystemPropertiesSource implements
475: JNDIPropertiesSource {
476:
477: public SystemPropertiesSource() {
478: super ();
479: }
480:
481: public String getProperty(final String propName) {
482: return System.getProperty(propName);
483: }
484: }
485:
486: /*
487: * Wrapper class for applet parameters source.
488: */
489: private static class AppletParametersSource implements
490: JNDIPropertiesSource {
491:
492: private Applet applet;
493:
494: public AppletParametersSource(Applet applet) {
495: this .applet = applet;
496: }
497:
498: public String getProperty(final String propName) {
499: return applet.getParameter(propName);
500: }
501: }
502:
503: }
|