Bug 3669 - ExceptionInInitializerError when instantiating ClientSecurityDescriptor
: ExceptionInInitializerError when instantiating ClientSecurityDescriptor
Status: RESOLVED FIXED
: CoG jglobus
utils
: unspecified
: PC Linux
: P3 normal
: ---
Assigned To:
:
:
:
:
  Show dependency treegraph
 
Reported: 2005-08-19 11:19 by
Modified: 2005-12-05 23:35 (History)


Attachments


Note

You need to log in before you can comment on or make changes to this bug.


Description From 2005-08-19 11:19:01
Results: The I18n class when constructed without a passed in class loader uses
a
context class loader from the current thread. I believe this to be wrong.
Instead I would suggest that if no class loader is specified then invoke the
one-argument ResourceBunder constructor which internally determines the correct
class loader to use. See modified source for org.globus.util.I18n.

The following information consists of several files starting with
"*****<filename>*****, please cut them out and paste them into the specified
files:

*****GT4Problem1/README*****
Assumptions: ANT is available, and JDK is available, and Globus is installed in
/usr/local/globus-4.0.0


1. Run: ant
        This creates a build/lib directory with three jar files in it.
        a. GT4Problem1A.jar - the jar with a main class that sets up a class
loader for GT4 and loads a class in GT4Problem1B
        b. GT4Problem1B.jar - the jar containing the loaded class that accesses
and uses GT4
        c. cog-jglobus-mod.jar.orig - the jar with a proposed fix to
org.globus.util.I18n. Note: name should not in ".jar"


2. Change to the build/lib directory: cd build/lib


3. Run the program: java -jar GT4Problem1A.jar . /usr/local/gt4.0.0/lib
        the two directories are (1) the problem jars directory and (2) the
globus jars directory
        which should fail with an ExceptionInInitializerError due the wrong
class loader being used
        for loading resource bundles during the construction of a
ClientSecurityDescriptor instance.


Exception in thread "main" java.lang.ExceptionInInitializerError
        at com.ibm.gt4problem1b.GT4Problem1Loaded.run(Unknown Source)
        at com.ibm.gt4problem1a.GT4Problem1.run(Unknown Source)
        at com.ibm.gt4problem1a.GT4Problem1.main(Unknown Source)
Caused by: java.util.MissingResourceException: Can't find bundle for base name
org.globus.wsrf.impl.security.descriptor.errors, locale en_US
        at
java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:804)
        at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:773)
        at java.util.ResourceBundle.getBundle(ResourceBundle.java:661)
        at org.globus.util.I18n.getI18n(I18n.java:71)
        at org.globus.util.I18n.getI18n(I18n.java:50)
        at
org.globus.wsrf.impl.security.descriptor.ClientSecurityDescriptor.<clinit>(ClientSecurityDescriptor.java:39)
        ... 3 more


4. Rename cog-jglobus-mod.jar.orig to cog-jglobus-mod.jar
        This allows the modified org.globus.util.I18n to be loaded before the
original one.


5. Rerun the program and you should get a "succeeded" message and no Error
should be thrown.
        ClientSecurityDescriptor construction succeeded


Note: for more diagnostic output turn on DEBUG and/or TRACE static final
variables.

Results: The I18n class when constructed without a passed in class loader uses
a
context class loader
        from the current thread. I believe this to be wrong. Instead I would
suggest that if no class
        loader is specified then invoke the one-argument ResourceBunder
constructor which internally
        determines the correct class loader to use. See modified source for
org.globus.util.I18n.


*****GT4Problem1/src/com/ibm/gt4problem1a/GT4Problem1.java*****
package com.ibm.gt4problem1a;

import java.io.File;
import java.io.FilenameFilter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * This is the GT4 Problem class. It is the main class and should be run from a
 * java command prompt. The main requires two arguments: the directory
 * containing the GT4 problem jar files, and the directory containing the GT4
 * jar files (typically /usr/local/globus-4.0.0). The program than scans these
 * directories and creates a class loader containing URLs for the directory and
 * each jar or zip file within the directory.
 * 
 * By default, the program should fail with an ExceptionInInitializerError due
 * to a problem in the org.globus.I18n class. A proposed correction is provide
 * in the cog-jglobus-mod.jar.orig file. To use the proposed correction rename
 * cog-jglobus-mod.jar.orig to cog-jglobus-mod.jar and re-run the program.
 * 
 * @author bwatt
 */
public class GT4Problem1 {

    public static final boolean TRACE = false;

    public static final boolean DEBUG = false;

    private String classpath = "";

    public static void main(String[] args) {
        if (TRACE)
            System.out.println("Entering GT4Problem1.main");
        GT4Problem1 x = new GT4Problem1(args);
        x.run();
        if (TRACE)
            System.out.println("Exiting GT4Problem1.main");
    }

    public GT4Problem1(String[] args) {

        if (TRACE)
            System.out.println("Entering GT4Problem1.ctor");

        if (args.length < 2) {
            System.err.println("java " + this.getClass().getName()
                    + " <GT4Problem1 lib directory> <GT4 lib directory>");
            if (TRACE)
                System.out.println("Exiting GT4Problem1.ctor");
            System.exit(1);
        }

        // Decode arguments and create a pseudo-classpath to be used by the run
        // method. The first is the library where to find the GT4Problem1 jar
        // files, and the second is where to find the GT4 jar files.
        classpath += args[0];
        classpath += System.getProperty("path.separator");
        classpath += args[1];
        if (DEBUG)
            System.out.println("In GT4Problem1.ctor classpath=" + classpath);

        if (TRACE)
            System.out.println("Exiting GT4Problem1.ctor");

    }

    private void run() {

        if (TRACE)
            System.out.println("Entering GT4Problem1.run");

        try {

            // Create a file name filter that finds only jar and zip files
            EndsWithFilenameFilter ewfnf = new EndsWithFilenameFilter();
            ewfnf.addEnding(".jar");
            ewfnf.addEnding(".zip");

            // Parse the pseudo-classpath and for each entry: first add the
            // directory to the vector, then add each jar/zip file to the
vector
            StringTokenizer st = new StringTokenizer(classpath, System
                    .getProperty("path.separator"));
            Vector cp_urls = new Vector();
            while (st.hasMoreTokens()) {
                String entry = st.nextToken();
                File entryFile = new File(entry);
                if (entryFile.isDirectory()) {
                    if (DEBUG)
                        System.out.println("In GT4Problem1.run entry=" + entry
                                + " isDirectory");
                    cp_urls.addElement(entryFile.toURL());
                    File[] jars = entryFile.listFiles(ewfnf);
                    if (jars != null) {
                        for (int i = 0; i < jars.length; i++) {
                            cp_urls.addElement(jars[i].toURL());
                        }
                    }
                } else if (entryFile.isFile()) {
                    if (DEBUG)
                        System.out.println("In GT4Problem1.run entry=" + entry
                                + " isFile");
                    cp_urls.addElement(entryFile.toURL());
                }
            }

            // Create an array of URLs based upon the number of items in the
            // vector that was just created. Then fill the array's contents
from
            // the vector's contents.
            URL[] classpathURLs = new URL[cp_urls.size()];
            int i = 0;
            Iterator iter = cp_urls.iterator();
            while (iter.hasNext()) {
                classpathURLs[i] = (URL) iter.next();
                if (DEBUG)
                    System.out.println("In GT4Problem1.run classpathURLs[" + i
                            + "] = " + classpathURLs[i]);
                i++;
            }

            // Create a class loader with the array of URLs and the parent
class
            // loader being the current class' class loader
            URLClassLoader urlClassLoader = new URLClassLoader(classpathURLs,
                    this.getClass().getClassLoader());

            if (DEBUG) {
                // Dump out Class Loaders (a diagnostic)
                System.out

.println("--------------------------------------------------------------------");
                ClassLoader cl = urlClassLoader;
                System.out.println("In GT4Problem1.run cl=" + cl);
                while (cl != null) {
                    if (cl instanceof URLClassLoader) {
                        URLClassLoader urlcl = (URLClassLoader) cl;
                        URL[] urls = urlcl.getURLs();
                        if (urls != null) {
                            for (int ii = 0; ii < urls.length; ii++) {
                                System.out.println("In GT4Problem1.run urls["
                                        + ii + "]=" + urls[ii]);
                            }
                        }
                    }
                    cl = cl.getParent();
                    System.out.println("In GT4Problem1.run cl=" + cl);
                }
                System.out

.println("--------------------------------------------------------------------");
            }

            // Load the GT4 Problem 1 class using the class loader. This class
            // should be in the GT4 Problem1 jar files
            Class loadedClass = urlClassLoader
                    .loadClass("com.ibm.gt4problem1b.GT4Problem1Loaded");
            if (DEBUG)
                System.out.println("In GT4Problem1.run loadedClass="
                        + loadedClass);

            // Create a instance of the loaded class
            Runnable loaded = (Runnable) loadedClass.newInstance();
            if (DEBUG)
                System.out.println("In GT4Problem1.run loaded=" + loaded);

            // Invoke the run method on the loaded class which tries to access
            // GT4 jar files by constructing a ClientSecurityDescriptor. If it
            // works this class should print a "succeeded" message, otherwise
it
            // throws an Error.
            loaded.run();

        } catch (Exception e) {
            e.printStackTrace();
        }

        if (TRACE)
            System.out.println("Exiting GT4Problem1.run");

    }

    private class EndsWithFilenameFilter implements FilenameFilter {

        private Vector endings = new Vector();

        public void addEnding(String ending) {
            endings.addElement(ending);
        }

        public boolean accept(File path, String name) {
            if (TRACE)
                System.out
                        .println("Entering
GT4Problem1.EndsWithFilenameFilter.accept path="
                                + path + " name=" + name);
            boolean result = false;
            Iterator i = endings.iterator();
            while (i.hasNext()) {
                String s = (String) i.next();
                if (name.endsWith(s))
                    result |= true;
            }
            if (TRACE)
                System.out
                        .println("Exiting
GT4Problem1.EndsWithFilenameFilter.accept result="
                                + result);
            return result;
        }
    }

}

*****GT4Problem1/src/com/ibm/gt4problem1b/GT4Problem1Loaded.java*****
package com.ibm.gt4problem1b;

import java.net.URL;
import java.net.URLClassLoader;

import org.globus.wsrf.impl.security.descriptor.ClientSecurityDescriptor;

/**
 * This is the GT4 Problem1 Loaded class which is loaded by the GT4 Problem1
 * class using a class loader. Internally it creates an instance of a
 * ClientSecurityDescriptor class which is part of the GT4 library. Since the
 * GT4 libraries are a part of the current class loader's path it should all
 * work, but due to a problem in the I18n class it fails.
 * 
 * @author bwatt
 */
public class GT4Problem1Loaded implements Runnable {

    public static final boolean TRACE = false;

    public static final boolean DEBUG = false;

    public void run() {

        if (TRACE)
            System.out.println("Entering GT4Problem1Loaded.run");

        if (DEBUG) {
            // Dump out Class Loaders (a diagnostic)
            System.out

.println("--------------------------------------------------------------------");
            ClassLoader cl = this.getClass().getClassLoader();
            System.out.println("In GT4Problem1Loaded.run cl=" + cl);
            while (cl != null) {
                if (cl instanceof URLClassLoader) {
                    URLClassLoader urlcl = (URLClassLoader) cl;
                    URL[] urls = urlcl.getURLs();
                    if (urls != null) {
                        for (int i = 0; i < urls.length; i++) {
                            System.out.println("In GT4Problem1Loaded.run urls["
                                    + i + "]=" + urls[i]);
                        }
                    }
                }
                cl = cl.getParent();
                System.out.println("In GT4Problem1Loaded.run cl=" + cl);
            }
            System.out

.println("--------------------------------------------------------------------");
        }

        // This will fail to work with an ExceptionInInitializerError
        // Because the I18n class does not honor the current class loader
        // Instead it uses the context class loader from the current thread
        // which does not contain the Globus Toolkit jar files and
        // therefore it cannot find a requested resource bundle.
        ClientSecurityDescriptor csd = new ClientSecurityDescriptor();

        System.out.println("ClientSecurityDescriptor construction succeeded");

        if (TRACE)
            System.out.println("Exiting GT4Problem1Loaded.run");

    }

}

*****GT4Problem1/src/org/globus/util/I18n.java*****
/*
 * Portions of this file Copyright 1999-2005 University of Chicago
 * Portions of this file Copyright 1999-2005 The University of Southern
California.
 *
 * This file or a portion of this file is licensed under the
 * terms of the Globus Toolkit Public License, found at
 * http://www.globus.org/toolkit/download/license.html.
 * If you redistribute this file, with or without
 * modifications, you must include this notice in the file.
 */
package org.globus.util;

import java.util.Map;
import java.util.HashMap;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.Locale;
import java.util.MissingResourceException;

/**
 * An utility class for internationalized message handling. Example usage::
 * <pre>
 *         private static I18n i18n =
I18n.getI18n(&quot;org.globus.resource&quot;);
 *         ...
 *         public void hello() {
 *            String filename = &quot;file1&quot;;
 *            String msg = i18n.getMessage(&quot;noFile&quot;, new
String[]{filename});
 *            ...
 *         }
 * </pre>
 */
public class I18n {

    private static Map mapping = new HashMap();

    private ResourceBundle messages = null;

    protected I18n(ResourceBundle messages) {
        this.messages = messages;
    }

    /**
     * Retrieve a I18n instance by resource name.
     * 
     * @param resource
     *            resource name. See {@linkResourceBundle#getBundle(String)
     *            ResourceBundle.getBundle()}
     */
    public static synchronized I18n getI18n(String resource) {
        return getI18n(resource, null);
    }

    /**
     * Retrieve a I18n instance by resource name
     * 
     * @param resource
     *            resource name. See {@linkResourceBundle#getBundle(String)
     *            ResourceBundle.getBundle()}
     * @param loader
     *            the class loader to be used to load the resource. This
     *            parameter is only used initially to load the actual resource.
     *            Once the resource is loaded, this argument is ignored.
     */
    public static synchronized I18n getI18n(String resource, ClassLoader
loader) {

        I18n instance = (I18n) mapping.get(resource);

        if (instance == null) {

            // Check if a class loader is specified
            if (loader == null) {

                // *ERROR* *ERROR* *ERROR* *ERROR* *ERROR* *ERROR* *ERROR*
                // Do not override the current class loader with the one from
                // the current thread, because if the thread's context class
                // loader does not contain the GT4 libraries then the resouce
                // bunder is not found causing a failure
                // loader = Thread.currentThread().getContextClassLoader();
                // *ERROR* *ERROR* *ERROR* *ERROR* *ERROR* *ERROR* *ERROR*

                // If no class loader is provided then get the resource bundle
                // without supplying a class loader. Internally the resource
                // bundle class gets the system class loader.
                instance = new I18n(ResourceBundle.getBundle(resource));

            } else {

                // If a class loader is provided then use it when getting the
                // resource bundle.
                instance = new I18n(ResourceBundle.getBundle(resource, Locale
                        .getDefault(), loader));

            }

            mapping.put(resource, instance);
        }
        return instance;
    }

    /**
     * Gets a message from resource bundle.
     */
    public String getMessage(String key) throws MissingResourceException {
        return messages.getString(key);
    }

    /**
     * Gets a formatted message from resource bundle
     */
    public String getMessage(String key, Object arg)
            throws MissingResourceException {
        return getMessage(key, new Object[] { arg });
    }

    /**
     * Gets a formatted message from resource bundle
     */
    public String getMessage(String key, Object[] vars)
            throws MissingResourceException {
        return MessageFormat.format(messages.getString(key), vars);
    }

}

*****GT4Problem1/build.properties*****
globus.location=/usr/local/globus-4.0.0

*****GT4Problem1/build.xml*****
<project name="GT4Problem1" default="all">

    <property environment="env"/>

    <property file="build.properties"/>
    <property file="${user.home}/build.properties"/>

    <property name="src.dir" value="${basedir}/src"/>
    <property name="build.dir" value="${basedir}/build"/>
    <property name="build.classes" value="${build.dir}/classes"/>
    <property name="build.lib" value="${build.dir}/lib"/>

    <path id="classpath">
        <fileset dir="${globus.location}/lib">
            <include name="*.jar"/>
        </fileset>
    </path>

   
<!--****************************************************************************-->

    <target name="createBuildTree">
        <mkdir dir="${build.classes}" />
        <mkdir dir="${build.lib}" />
    </target>

    <target name="compile" depends="createBuildTree">
        <javac srcdir="${src.dir}" destdir="${build.classes}"
classpathref="classpath"/>
    </target>

    <target name="jar" depends="compile">
        <jar basedir="${build.classes}" jarfile="${build.lib}/GT4Problem1A.jar"
includes="com/ibm/gt4problem1a/GT4Problem1*.class">
            <manifest>
                <attribute name="Main-Class"
value="com${file.separator}ibm${file.separator}gt4problem1a${file.separator}GT4Problem1"/>
            </manifest>
        </jar>
        <jar basedir="${build.classes}" jarfile="${build.lib}/GT4Problem1B.jar"
includes="com/ibm/gt4problem1b/GT4Problem1Loaded.class"/>
        <jar basedir="${build.classes}"
jarfile="${build.lib}/cog-jglobus-mod.jar.orig"
includes="org/globus/util/I18n.class"/>
    </target>

    <target name="all" depends="jar">
        <echo message="processing"/>
    </target>

   
<!--****************************************************************************-->

    <target name="clean">
        <delete dir="${build.dir}" />
    </target>

</project>

*****END OF FILES*****
------- Comment #1 From 2005-08-19 11:30:56 -------
Here is the overall environment that cause this situation.

A very large Java application consisting of numerous JAR files is running within
a web application server. This application needs to be enhanced to communicate
with Globus Toolkit 4.0. We had to insure all GT4 JAR files are present, and
would not conflict with any JAR files in the application, and would not
disrupting the current application. We choose to use a piece of code that
creates a class loader that includes all the GT4 JAR files from
/usr/local/globus-4.0.0/lib, it then loads a class we created that accesses and
uses GT4 extensively for maximum isolation. 

When we did this we found that the GT4 code was not "respecting" the class
loader we had set up and instead was using a different class loader when
accessing resource bundles. This different class loader was the context class
loader of the current thread and is did NOT contain any of the GT4 lib JAR
files. As a result, the program got an ExceptionInInitializerError in I18n class
when trying to instantiate a ResourceBundle.

In the previous comment we have provided a small test case that exposes this
problem and a potential solution as a modification to the org.globus.util.I18n
class found in cog-jglobus.jar file.

Sincerely,
Brian Watt
------- Comment #2 From 2005-10-03 12:50:20 -------
Relying on the default behaviour of ResourceBundle will work only if everything 
is loaded by one classloader. But it will not work if for example the i18n 
class is loaded by one classloader and the bundle exists in another 
classloader. That's the case in Tomcat for example (cog-jglobus.jar is loaded 
by common classloader but some bundle is loaded by the web application 
classloader). The only real fix is to use caller's classloader to load the 
bundle.
------- Comment #3 From 2005-10-03 16:19:27 -------
I committed fixes to the code to use the caller's classloader when loading the 
bundle. I also committed the updated cog-jglobus.jar to gt4 cvs to trunk. I 
will consider putting it into the globus_4_0_branch (to be part of 4.0.2 
release) once all the nightly tests run ok.

------- Comment #4 From 2005-10-04 12:20:37 -------
Committed the updated cog-jglobus.jar to globus_4_0_branch too.