Debug Classpath Issues
Overview
When testing your application, or especially when testing a new extension, those pesky classloading errors may rear their ugly head. The alakai system was developed from the ground up around a robust class loading architecture. If you've worked with application servers such as jboss, websphere, weblogic, etc ..., you know how frustrating it can be to track down classpath issues, e.g. ClassNotFound, ClassCastException and class LinkageErrors.
Well, we'd like to tell you that we've made all of those problems disappear. While we've done all we can to mitigate their occurrence, they are unfortunately inevitable. What we hope to compile here is a list of tips and tricks that you can use to help you track down your specific problem. Before beginning, however, we strongly suggest that you familiarize yourself with the alakai class loading architecture. Once you understand the big picture, you'll have a much easier time finding and correcting the issue.
Debug Logging
The first step is to identify what specific artifacts are (or aren't) on the relevant classpath. To do this, you need to configure the logging system by modifying the log4j.xml file. Ideally you should encounter and fix these errors while testing. The following path, therefore, identifies the location within the context of a suite specific integration test which in this case is named suite1.

If you add the following entry to the log4j.xml file, the alakai dependency manager will output detailed logging to the log file/console. By analyzing the artifacts that are on your application/extension's classpath, you can hopefully identify either a duplicate version of the same artifact or a missing artifact.
<logger name="org.bluestemsoftware.specification.eoa.system.System"> <level value="DEBUG" /> </logger>
Here's a sample output which identifies the artifacts which are on the standalone-10-server extension's deployment classloader's classpath. It also lists the artifacts which are on the shared classloader's classpath.
12:32:40,125 INFO [System] modeled system dependencies 12:32:40,135 DEBUG [System] dependencies on shared classpath: specification-eoa-ext-api:SNAPSHOT:jar commons-logging:1.1:jar geronimo-ejb_2.1_spec:1.0.1:jar geronimo-j2ee-management_1.1_spec:1.0.1:jar geronimo-j2ee-connector_1.5_spec:2.0.0:jar geronimo-jta_1.1_spec:1.1.1:jar geronimo-jms_1.1_spec:1.1.1:jar geronimo-servlet_2.5_spec:1.1:jar jsp-api-2.1:6.1.5:jar geronimo-activation_1.1_spec:1.0.2:jar jaas-login-module:SNAPSHOT:jar 12:32:40,145 INFO [System] loading factory deployments ... 12:32:40,546 DEBUG [System] standalone-10-server:SNAPSHOT:eoa-factory scoped dependencies: > xerces-impl:2.9.0:jar > open-eoa-commons:SNAPSHOT:jar
Remember that dependencies with 'compile' scope are resolved transitively. So ... if an artifact shows up in the list, and you are unsure how it ended up there, by enabling 'trace' logging, you can follow the dependency trail and exclude the offending artifact if necessary.
<logger name="org.bluestemsoftware.specification.eoa.system.System"> <level value="TRACE" /> </logger>
Here's an example which shows the trace level output and which identifies the dependency trail for the artifacts listed on the standalone-10-server extension's deployment classpath. note that 'dummy' represents the root project, i.e. the extension's POM.
13:36:33,597 TRACE [System] loading org.bluestemsoftware.open.eoa.ext/standalone-10-server/SNAPSHOT/eoa-factory 13:36:33,808 DEBUG [System] standalone-10-server:SNAPSHOT:eoa-factory scoped dependencies: > xerces-impl:2.9.0:jar dependency trail - [dummy:dummy:jar:dummy, org.bluestemsoftware.open.maven.tparty:xerces-impl:jar:2.9.0] > open-eoa-commons:SNAPSHOT:jar dependency trail - [dummy:dummy:jar:dummy, org.bluestemsoftware.open.eoa.shared:open-eoa-commons:jar:SNAPSHOT] 13:36:33,808 TRACE [System] loadExtensionFactoryDeployment end
ClassNotFoundException
This should be the easiest type of error to fix. It simply means that your deployment classloader is unable to find a required class file. Enable logging as described in the previous section and verify that a dependent artifact is missing from the classpath. Perhaps you failed to define the correct scope within your project's POM. For example, artifacts scoped to the deployment's classloader must define scope 'compile' and optional set to 'true'. You did read the section on dependency management within the user guide - right?
ClassCastException
OK, this one's a little harder to diagnose. You've checked your code and you're certain that your casting the instance to the corrrect class. Here are some likely scenarios.
TODO
LinkageErrors
These are probably the hardest to diagnose, but never fear. This type of error implies that the class has been located by the classloader, but there is something wrong in regards to its definition. Here are some specific scenarios that we've encountered and how we solved them.
NoClassDefFoundError
When your application/extension loads you get a NoClassDefFoundError. This cause for this type of error is different from a ClassNotFoundException. This error implies that the class has been found, but that it's definition is incompatible with that used to compile the referencing class. This problem can manifest itself in different ways.
Missing Classes
This error can be caused by an artifact which is missing from the classpath. While you might expect a ClassNotFoundException, this error can show up if a static initializer or static member is used within the referencing class. To correct the error, determine which artifact contains the missing class def and add it to the deployment classpath, e.g. by removing the exclusion, etc ... If this doesn't solve the problem, perhaps it's a non-compliant specification artifact. Read on.
Bad Specification
OK, this is an obscure one, which is really hard to diagnose - unless of course you're reading this first. So you get a NoClassDefFoundError and you're certain the artifact which contains the offending class is scoped to your deployment classloader. What may be happening is that the SharedClassLoader is being asked to load a class for which it has no definition.
So here's the scenario, you've added a specification artifact to the shared classpath, i.e. an artifact that defines an API and no implementation. The corresponding implementation artifact is scoped to the deployment classloader. Life is good. The problem occurs when the specification API artifact contains one or more classes which reference one or more classes within the implementation artifact.
The JVM uses the 'defining' classloader to load classes referenced by a class. If a class loaded by the shared classloader references a class within an implementation artifact scoped to a deployment classloader, the JVM will delegate the request to the shared classloader rather than the deployment classloader -> NoClassDefFoundError.
The only long term solution to this problem is to submit a feature request requesting that authoring organization refactor any artifact that violates the above guidelines. A short term solution would be to fix the problem yourself and submit a patch. Or scope the API artifact to the deployment classloader - which means you can't share the API across deployments.
Duplicate Classes
A NoClassDefFoundError can also be caused by duplicate instances of the same class being loaded by different classloaders within the classloading hierarchy. In the following example, the missing definition is for the class org/apache/commons/logging/LogFactory. You see the containing artifact commons-logging:1.1:jar on the classpath as identified in the following logging output:
axis-server-feature:SNAPSHOT:eoa-feature scoped dependencies: > commons-logging:1.1:jar > xalan:2.7.0:jar > axis2-kernel:1.3:jar > axis2-json:1.3:jar > activation:1.1:jar > servlet-api:2.3:jar > axiom-api:1.2.5:jar > stax-api:1.0.1:jar > wstx-asl:3.2.1:jar > axiom-impl:1.2.5:jar > mail:1.4:jar > axiom-dom:1.2.5:jar > commons-io:1.2:jar > xml-apis:1.3.03:jar > jettison:1.0-RC1:jar > jaxen:1.1-beta-9:jar > xercesImpl:2.8.1:jar
But, according to the error, the definition against which the referencing class was compiled is different from the one loaded by the classloader. Turns out there is a duplicate commons-logging artifact on the classpath as shown within the logged shared classpath's entries.
dependencies on shared classpath: specification-eoa-ext-api:SNAPSHOT:jar commons-logging:1.1:jar geronimo-ejb_2.1_spec:1.0.1:jar geronimo-j2ee-management_1.1_spec:1.0.1:jar geronimo-j2ee-connector_1.5_spec:2.0.0:jar geronimo-jta_1.1_spec:1.1.1:jar geronimo-jms_1.1_spec:1.1.1:jar geronimo-servlet_2.5_spec:1.1:jar jsp-api-2.1:6.1.5:jar geronimo-activation_1.1_spec:1.0.2:jar jaas-login-module:SNAPSHOT:jar
So we need to exclude the commons-logging:1.1:jar artifact, but which dependency references it? Based upon the following trace logging output, we need to exclude the dependency from the org.apache.axis2:axis2-json dependency which is defined as a dependency with 'compile' scope in the axis-server-feature POM.
axis-server-feature:SNAPSHOT:eoa-feature scoped dependencies: > commons-logging:1.1:jar dependency trail - [dummy:dummy:jar:dummy, org.apache.axis2:axis2-json:jar:1.3, org.apache.ws.commons.axiom:axiom-api:jar:1.2.5, commons-logging:commons-logging:jar:1.1] ...