JBoss.org Community Documentation

3.2.2.2. IllegalAccessException - Doing what you should not

A java.lang.IllegalAccessException is thrown when one attempts to access a method or member that visibility qualifiers do not allow. Typical examples are attempting to access private or protected methods or instance variables. Another common example is accessing package protected methods or members from a class that appears to be in the correct package, but is really not due to caller and callee classes being loaded by different class loaders. An example of this is illustrated by the code shown in Example 3.4, “The ExIAEd class used to demonstrate IllegalAccessException due to duplicate class loaders”.

package org.jboss.book.jmx.ex0;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.Method;

import org.apache.log4j.Logger;

import org.jboss.util.ChapterExRepository;
import org.jboss.util.Debug;

/**
 * An example of IllegalAccessExceptions due to
 * classes loaded by two class loaders.
 * @author Scott.Stark@jboss.org
 * @version $Revision: 1.9 $
 */
public class ExIAEd
{
    public static void main(String[] args) throws Exception
    {
	ChapterExRepository.init(ExIAEd.class);

	String chapDir = System.getProperty("j2eechapter.dir");
	Logger ucl0Log = Logger.getLogger("UCL0");
	File jar0 = new File(chapDir+"/j0.jar");
	ucl0Log.info("jar0 path: "+jar0.toString());
	URL[] cp0 = {jar0.toURL()};
	URLClassLoader ucl0 = new URLClassLoader(cp0);
	Thread.currentThread().setContextClassLoader(ucl0);
	
	StringBuffer buffer = new
	    StringBuffer("ExIAEd Info");
	Debug.displayClassInfo(ExIAEd.class, buffer, false);
	ucl0Log.info(buffer.toString());
	
	Class ctxClass1 = ucl0.loadClass("org.jboss.book.jmx.ex0.ExCtx");
	buffer.setLength(0);
	buffer.append("ExCtx Info");
	Debug.displayClassInfo(ctxClass1, buffer, false);
	ucl0Log.info(buffer.toString());
	Object ctx0 = ctxClass1.newInstance();

	try {
	    Class[] types = {Object.class};
	    Method useValue =
		ctxClass1.getDeclaredMethod("pkgUseValue", types);
	    Object[] margs = {null};
	    useValue.invoke(ctx0, margs);
	} catch(Exception e) {
	    ucl0Log.error("Failed to invoke ExCtx.pkgUseValue", e);
	}
    }
}

Example 3.4. The ExIAEd class used to demonstrate IllegalAccessException due to duplicate class loaders


The ExIAEd.main method uses reflection to load the ExCtx class via the ucl0 class loader while the ExIEAd class was loaded by the application class loader. We will run this example to demonstrate how the IllegalAccessException can occur and then look at the specific issue with the example. Run the example using the following command:

[examples]$ ant -Dchap=jmx -Dex=0d run-example
Buildfile: build.xml
...
[java] java.lang.IllegalAccessException: Class org.jboss.book.jmx.ex0.ExIAEd 
  can not access a member of class org.jboss.book.jmx.ex0.ExCtx with modifiers ""
[java]     at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
[java]     at java.lang.reflect.Method.invoke(Method.java:578)
[java]     at org.jboss.book.jmx.ex0.ExIAEd.main(ExIAEd.java:48)

The truncated output shown here illustrates the IllegalAccessException. The full output can be found in the logs/jmx-ex0d.log file. At line 48 of ExIAEd.java the ExCtx.pkgUseValue(Object) method is invoked via reflection. The pkgUseValue method has package protected access and even though both the invoking class ExIAEd and the ExCtx class whose method is being invoked reside in the org.jboss.book.jmx.ex0 package, the invocation is seen to be invalid due to the fact that the two classes are loaded by different class loaders. This can be seen by looking at the debugging output from the jmx-ex0d.log file.

[INFO,UCL0] ExIAEd Info
org.jboss.book.jmx.ex0.ExIAEd(7808b9).ClassLoader=sun.misc.Launcher$AppClassLoader@a9c85c
..sun.misc.Launcher$AppClassLoader@a9c85c
...
[INFO,UCL0] ExCtx Info
org.jboss.book.jmx.ex0.ExCtx(64c34e).ClassLoader=java.net.URLClassLoader@a9c85c
..java.net.URLClassLoader@5d88a
...

The ExIAEd class is seen to have been loaded via the default application class loader instance sun.misc.Launcher$AppClassLoader@a9c85c, while the ExCtx class was loaded by the java.net.URLClassLoader@a9c85c instance. Because the classes are loaded by different class loaders, access to the package protected method is seen to be a security violation. So, not only is type a function of both the fully qualified class name and class loader, the package scope is as well.

An example of how this can happen in practice is to include the same classes in two different SAR deployments. If classes in the deployment have a package protected relationship, users of the SAR service may end up loading one class from SAR class loading at one point, and then load another class from the second SAR at a later time. If the two classes in question have a protected access relationship an IllegalAccessError will result. The solution is to either include the classes in a separate jar that is referenced by the SARs, or to combine the SARs into a single deployment. This can either be a single SAR, or an EAR that includes both SARs.