IllegalAccessError, packages and classloaders

Ever wondered why you get an IllegalAccessError when invoking protected/package-private methods on class B from class A although both classes are in the same package?

Well it mostly happens only at runtime when two different classloaders loading the classes. classloader A loads class A, classloader B loads class B. Since the JVM spec, paragraph 5.3 mandates

At runtime, a class or interface is determined not by its name alone, but by a pair: its binary name (§4.2.1) and its defining class loader. Each such class or interface belongs to a single runtime package. The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface.

the package from class A is the same as class B at compile time, but NOT at runtime since it was loaded by different classloaders! To get to the root cause, you have to check why your classes are loaded by different classloaders. For example I came across that problem when using JBoss 7 the first time.

JBoss 7.1 and java.lang.IllegalAccessError invoking private methods

At my company we’re starting to use JBoss 7.1 and came across a problem with its new classloading behavior. We’re deploying an EAR with the following structure:

  • EAR
    • lib
      • common.jar
    • ejb.jar

In common.jar are classes included, that have a package-private constructor that should only be used within ejb.jar’s factory. For this the factory and the specific classes are located in the same package, but in different jar’s. So a client can only use the public constructor of the classes whereas the ejb.jar can invoke the internal contructor not visible to the client.

With JBoss 5.1.0 this worked without a problem. But when we tried the same with JBoss 7.1, we encountered the following Error:

Caused by: java.lang.IllegalAccessError: tried to access method CommonClass.<init>()V from class FactoryClass

This is due to the fact that with JBoss 7 each jar is loaded by a separate classloader. We tried the suggested workaround using the jboss-deployment-structure.xml mentioned in a similar JIRA ticket, but always ended up in two separate classloaders for the jar’s.

Since it is a limitation of the underyling JVM and not JBoss 7 we simply added the factory to the common.jar and created a common-client.jar via maven assembly where the factory is excluded. Not nice, but helped us to go on.