/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.jndi.internal; import java.applet.Applet; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; import javax.naming.ConfigurationException; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.ldap.LdapContext; import org.apache.harmony.jndi.internal.nls.Messages; /** * This is a utility class that reads environment properties. */ public final class EnvironmentReader { // The name of application resource files private static final String APPLICATION_RESOURCE_FILE = "jndi.properties"; //$NON-NLS-1$ // The name of provider resource file private static final String PROVIDER_RESOURCE_FILE = "jndiprovider.properties"; //$NON-NLS-1$ // Not allowed to create an instance private EnvironmentReader() { super(); } /* * Merge additional properties with already read ones. * * @param src - the source containing additional properties @param dst - the * destination to put additional properties @param valueAddToList - whether * to add new values of C-type properties */ public static void mergeEnvironment(final Hashtable<?, ?> src, final Hashtable<Object, Object> dst, final boolean valueAddToList) { Object key = null; String val = null; Enumeration<?> keys = src.keys(); while (keys.hasMoreElements()) { key = keys.nextElement(); if (!dst.containsKey(key)) { /* * If this property doesn't exist yet, add it. */ dst.put(key, src.get(key)); } else if (valueAddToList && (LdapContext.CONTROL_FACTORIES.equals(key) || Context.OBJECT_FACTORIES.equals(key) || Context.STATE_FACTORIES.equals(key) || Context.URL_PKG_PREFIXES .equals(key))) { /* * Otherwise, if this property can contain a list of values, add * the additional values if the flag "valueAddToList" is true. */ // Read the original value val = (String) dst.get(key); // Values are combined into a single list separated by colons. val = val + ":" + src.get(key); //$NON-NLS-1$ // The final value becomes the resulting value of that property dst.put(key, val); } else { /* * Otherwise, ignore the found value. */ } } } /* * Get the required 7 JNDI properties from JNDI properties source. This * method is designed as package visibility to improve performance when * called by anonymous inner classes. * * @return a hashtable holding the required properties. */ static Hashtable<Object, Object> filterProperties( final JNDIPropertiesSource source) { final Hashtable<Object, Object> filteredProperties = new Hashtable<Object, Object>(); String propValue = null; propValue = source.getProperty(Context.INITIAL_CONTEXT_FACTORY); if (null != propValue) { filteredProperties.put(Context.INITIAL_CONTEXT_FACTORY, propValue); } propValue = source.getProperty(Context.DNS_URL); if (null != propValue) { filteredProperties.put(Context.DNS_URL, propValue); } propValue = source.getProperty(Context.PROVIDER_URL); if (null != propValue) { filteredProperties.put(Context.PROVIDER_URL, propValue); } propValue = source.getProperty(Context.OBJECT_FACTORIES); if (null != propValue) { filteredProperties.put(Context.OBJECT_FACTORIES, propValue); } propValue = source.getProperty(Context.STATE_FACTORIES); if (null != propValue) { filteredProperties.put(Context.STATE_FACTORIES, propValue); } propValue = source.getProperty(Context.URL_PKG_PREFIXES); if (null != propValue) { filteredProperties.put(Context.URL_PKG_PREFIXES, propValue); } propValue = source.getProperty(LdapContext.CONTROL_FACTORIES); if (null != propValue) { filteredProperties.put(LdapContext.CONTROL_FACTORIES, propValue); } return filteredProperties; } /* * Read the required 7 JNDI properties from system properties and merge with * existing properties. Note that the values of C-type properties are only * included when no corresponding value is presented in existing properties. * * @param existingProps - existing properties */ public static void readSystemProperties( final Hashtable<Object, Object> existingProps) { /* * Privileged code is used to access system properties. This is required * if JNDI is run in Applet or other applications which only have * limited permissions to access certain resources. */ Hashtable<Object, Object> systemProperties = AccessController .doPrivileged(new PrivilegedAction<Hashtable<Object, Object>>() { public Hashtable<Object, Object> run() { return filterProperties(new SystemPropertiesSource()); } }); mergeEnvironment(systemProperties, existingProps, false); } /* * Read the required 7 JNDI properties from applet parameters and merge with * existing properties. Note that the values of C-type properties are only * included when no corresponding value is presented in existing properties. * * @param applet - the applet object @param existingProps - existing * properties */ public static void readAppletParameters(Object applet, Hashtable<Object, Object> existingProps) { if (null != applet) { Hashtable<Object, Object> appletParameters = filterProperties(new AppletParametersSource( (Applet) applet)); mergeEnvironment(appletParameters, existingProps, false); } } /* * Read multiple resource files from the classpaths given the file name. * This method is designed as package visibility to improve performance when * called by anonymous inner classes. * * @param name - the name of the resource file @param existingProps - * existing properties, cannot be null @param filter - to filter properties */ static Hashtable<Object, Object> readMultipleResourceFiles( final String name, final Hashtable<Object, Object> existingProps, ClassLoader cl) throws NamingException { if (null == cl) { cl = ClassLoader.getSystemClassLoader(); } Enumeration<URL> e = null; try { // Load all resource files e = cl.getResources(name); } catch (IOException ex) { // Unexpected ClassLoader exception // jndi.23=Failed to load JNDI resource files. ConfigurationException newEx = new ConfigurationException(Messages .getString("jndi.23")); //$NON-NLS-1$ newEx.setRootCause(ex); throw newEx; } // Read all the loaded properties and merge URL url = null; InputStream is = null; final Properties p = new Properties(); while (e.hasMoreElements()) { url = e.nextElement(); try { if (null != (is = url.openStream())) { p.load(is); mergeEnvironment(p, existingProps, true); p.clear(); } } catch (IOException ex) { // Can't read this resource file // jndi.24=Failed to read JNDI resource files. ConfigurationException newEx = new ConfigurationException( Messages.getString("jndi.24")); //$NON-NLS-1$ newEx.setRootCause(ex); throw newEx; } finally { try { if (null != is) { is.close(); } } catch (IOException ex) { // Ignore closing exception } finally { is = null; } } } return existingProps; } /* * Read application/applet resource files. * * @param existingProps - existing properties, cannot be null. */ public static Hashtable<Object, Object> readApplicationResourceFiles( final Hashtable<Object, Object> existingProps) throws NamingException { // Use privileged code to read the application resource files try { AccessController .doPrivileged(new PrivilegedExceptionAction<Void>() { public Void run() throws NamingException { readMultipleResourceFiles( APPLICATION_RESOURCE_FILE, existingProps, Thread.currentThread() .getContextClassLoader()); return null; } }); } catch (PrivilegedActionException e) { Exception rootCause = e.getException(); if (rootCause instanceof NamingException) { throw (NamingException) rootCause; } else if (rootCause instanceof RuntimeException) { throw (RuntimeException) rootCause; } else { // This should not happen. } } return existingProps; } /* * Read the properties file "java.home"/lib/jndi.properties. Pay attention * to the privileged code for accessing this external resource file. This is * required if JNDI is run in Applet or other applications which only have * limited permissions to access certain resources. * * @param existingProps - existing properties, cannot be null. */ public static Hashtable<Object, Object> readLibraryResourceFile( final Hashtable<Object, Object> existingProps) throws NamingException { final String sep = System.getProperty("file.separator"); //$NON-NLS-1$ String resPath = null; // Construct the full filename of "java.home"/lib/jndi.properties resPath = AccessController.doPrivileged(new PrivilegedAction<String>(){ public String run() { return System.getProperty("java.home"); //$NON-NLS-1$ } }); if (!resPath.endsWith(sep)) { resPath += sep; } resPath += "lib" + sep + APPLICATION_RESOURCE_FILE; //$NON-NLS-1$ // Try to read this properties if it exists InputStream is = null; final File resFile = new File(resPath); final Properties p = new Properties(); // Use privileged code to determine whether the file exists boolean resFileExists = AccessController.doPrivileged( new PrivilegedAction<Boolean>() { public Boolean run() { return Boolean.valueOf(resFile.exists()); } }).booleanValue(); if (resFileExists) { try { // Use privileged code to read the file is = AccessController .doPrivileged(new PrivilegedExceptionAction<FileInputStream>() { public FileInputStream run() throws IOException { FileInputStream localInputStream = new FileInputStream( resFile); p.load(localInputStream); return localInputStream; } }); mergeEnvironment(p, existingProps, true); } catch (PrivilegedActionException e) { // Can't read "java.home"/lib/jndi.properties // jndi.25=Failed to read JNDI resource files in java home // library. ConfigurationException newEx = new ConfigurationException( Messages.getString("jndi.25")); //$NON-NLS-1$ newEx.setRootCause(e.getException()); throw newEx; } finally { try { if (null != is) { is.close(); } } catch (IOException ex) { // Ignore closing exception } } } return existingProps; } /* * Read the service provider resource file. * * @param context - the context @param existingProps - existing properties, * cannot be null. */ public static Hashtable<Object, Object> readProviderResourceFiles( final Context context, final Hashtable<Object, Object> existingProps) throws NamingException { String factory = context.getClass().getName(); String resPath = null; int len = factory.lastIndexOf('.'); // Construct the full filename of the service provider resource file if (-1 == len) { // Default package resPath = PROVIDER_RESOURCE_FILE; } else { // Replace "." with '/' resPath = factory.substring(0, len + 1); resPath = resPath.replace('.', '/'); resPath += PROVIDER_RESOURCE_FILE; } // Use privileged code to read the provider resource files try { final String finalResPath = resPath; AccessController .doPrivileged(new PrivilegedExceptionAction<String>() { public String run() throws NamingException { readMultipleResourceFiles(finalResPath, existingProps, context.getClass() .getClassLoader()); return null; } }); } catch (PrivilegedActionException e) { Exception rootCause = e.getException(); if (rootCause instanceof NamingException) { throw (NamingException) rootCause; } else if (rootCause instanceof RuntimeException) { throw (RuntimeException) rootCause; } else { // This should not happen. throw new AssertionError(rootCause); } } return existingProps; } /* * Get the list of the specified factory names from the supplied environment * and the resource provider files of the given Context. * * @param envmt The supplied environment. @param ctx The Context whose * resource provider files will be read. @param key The name of the factory. * @return The list of the desired factory names. @throws NamingException If * an error occurs when reading the provider resource files. */ public static String[] getFactoryNamesFromEnvironmentAndProviderResource( Hashtable<?, ?> envmt, Context ctx, String key) throws NamingException { List<String> fnames = new ArrayList<String>(); // collect tokens from envmt with key if (null != envmt) { String str = (String) envmt.get(key); if (null != str) { StringTokenizer st = new StringTokenizer(str, ":"); //$NON-NLS-1$ while (st.hasMoreTokens()) { fnames.add(st.nextToken()); } } } // collect tokens from ctx's provider resource file if (null != ctx) { Hashtable<Object, Object> h = new Hashtable<Object, Object>(); // read provider resource file from ctx's package EnvironmentReader.readProviderResourceFiles(ctx, h); String str = (String) h.get(key); if (null != str) { StringTokenizer st = new StringTokenizer(str, ":"); //$NON-NLS-1$ while (st.hasMoreTokens()) { fnames.add(st.nextToken()); } } } // if key is Context.URL_PKG_PREFIXES, append "com.sun.jndi.url" and // "org.apache.harmony.jndi.provider" at the end if (Context.URL_PKG_PREFIXES.equals(key)) { fnames.add("com.sun.jndi.url"); //$NON-NLS-1$ fnames.add("org.apache.harmony.jndi.provider"); //$NON-NLS-1$ } // return factory names return fnames.toArray(new String[fnames.size()]); } /* * Wrapper interface for JNDI properties source. */ private interface JNDIPropertiesSource { // Get a JNDI property with the specified name String getProperty(final String propName); } /* * Wrapper class for system properties source. */ private static class SystemPropertiesSource implements JNDIPropertiesSource { public SystemPropertiesSource() { super(); } public String getProperty(final String propName) { return System.getProperty(propName); } } /* * Wrapper class for applet parameters source. */ private static class AppletParametersSource implements JNDIPropertiesSource { private Applet applet; public AppletParametersSource(Applet applet) { this.applet = applet; } public String getProperty(final String propName) { return applet.getParameter(propName); } } }