4.5. Dynamic Classloading

So far you have used the extension and application classloaders to load all of the classes in the application. The application classpath is set up by the run.sh script using the -cp flag to include the current directory and the client-1.0.0.jar, as shown here:
java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1
For convenience the JARs in the lib directory were added to the extension classloader's classpath using the java.ext.dirs system property, rather than listing the full path to each of the JARs after the -cp flag. Because the classloader extension is the parent of the classloader application, the client classes is able to find all of the Microcontainer classes and the Human Resources service classes at run-time.

Note

With Java versions 6 and higher, you can use a wild-card to include all JARs in a directory with the -cp flag: java -cp `pwd`/lib/*:.:client-1.0.0.jar org.jboss.example.client.Client $1
Here, all of the classes in the application will be added to the application classloader's classpath, and the extension classloader's classpath will retain its default value.
What happens if you need to deploy an additional service at run-time? If the new service is packaged in a JAR file, it must be visible to a classloader before any of its classes can be loaded. Because you have already set up the classpath for the application classloader (and extension classloader) on start-up, it is not easy to add the URL of the JAR. The same situation applies if the service classes are contained in a directory structure. Unless the top-level directory is located in the current directory (which is on the application classpath) then the classes will not be found by the application classloader.
If you wish to redeploy an existing service, changing some of its classes, you need to work around security constraints, which forbid an existing classloader from reloading classes.
The goal is to create a new classloader that knows the location of the new service's classes, or that can load new versions of an existing service's classes, in order to deploy the service's beans. JBoss Microcontainer uses the <classloader> element in the deployment descriptor to accomplish this.
The client-cl distribution contains the file listed in the Example 4.5, “Listing of the commandLineClient/target/client-cl Directory” .

Example 4.5. Listing of the commandLineClient/target/client-cl Directory

       |-- client-1.0.0.jar
       |-- jboss-beans.xml
       |-- lib
       |   |-- concurrent-1.3.4.jar
       |   |-- jboss-common-core-2.0.4.GA.jar
       |   |-- jboss-common-core-2.2.1.GA.jar
       |   |-- jboss-common-logging-log4j-2.0.4.GA.jar
       |   |-- jboss-common-logging-spi-2.0.4.GA.jar
       |   |-- jboss-container-2.0.0.Beta6.jar
       |   |-- jboss-dependency-2.0.0.Beta6.jar
       |   |-- jboss-kernel-2.0.0.Beta6.jar
       |   |-- jbossxb-2.0.0.CR4.jar
       |   |-- log4j-1.2.14.jar
       |   `-- xercesImpl-2.7.1.jar
       |-- otherLib
       |   `-- humanResourcesService-1.0.0.jar
       |`-- run.sh
The humanResourcesService.jar file has been moved to a new sub-directory called otherLib. It is no longer available to either the extension or application classloaders whose classpaths are setup in the run.sh script:
java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1
To work around this, create a new classloader during the deployment of the service, load it in the service classes, and create instances of the beans. To see how this is done, look at the contents of the jboss-beans.xml file:
<?xml version="1.0" encoding="UTF-8"?>

<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"            
	    xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd"
	    xmlns="urn:jboss:bean-deployer:2.0">

  <bean name="URL" class="java.net.URL">
    <constructor>
      <parameter>file:/Users/newtonm/jbossmc/microcontainer/trunk/docs/examples/User_Guide/gettingStarted/commandLineClient/target/client-cl.dir/otherLib/humanResourcesService-1.0.0.jar</parameter>
    </constructor>
  </bean>

  <bean name="customCL" class="java.net.URLClassLoader">
    <constructor>
      <parameter>
	<array>
	  <inject bean="URL"/> 
	</array>
      </parameter>
    </constructor>
  </bean>

  <bean name="HRService" class="org.jboss.example.service.HRManager">
    <classloader><inject bean="customCL"/></classloader>
    <!-- <property name="hiringFreeze">true</property>
	 <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> -->
  </bean>

  <!-- <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy">
       <property name="minSalary">1000</property>
       <property name="maxSalary">80000</property>
       </bean>

<bean name="LocationBasedSalary" class="org.jboss.example.service.util.LocationBasedSalaryStrategy">
<property name="minSalary">2000</property>
<property name="maxSalary">90000</property>
</bean> -->

</deployment>
		
		
		

  1. First, create an instance of java.net.URL called URL, using parameter injection in the constructor to specify the location of the humanResourcesService.jar file on the local file-system.
  2. Next, create an instance of a URLClassLoader by injecting the URL bean into the constructor as the only element in an array.
  3. Include a <classloader> element in your HRService bean definition and inject the customCL bean. This specifies that the HRManager class needs to be loaded by the customCL classloader.
You need a way to decide which classloader to use for the other beans in the deployment. All beans in the deployment use the current thread's context classloader. In this case the thread that handles deployment is the main thread of the application which has its context classloader set to the application classloader on start-up. If you wish, you can specify a different classloader for the entire deployment using a <classloader> element, as shown in Example 4.6, “Specifying a Different Classloader”.

Example 4.6. Specifying a Different Classloader

<?xml version="1.0" encoding="UTF-8"?>

<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"            
	    xsi:schemaLocation="urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd"
	    xmlns="urn:jboss:bean-deployer:2.0">

  <classloader><inject bean="customCL"/></classloader>

  <bean name="URL" class="java.net.URL">
    <constructor>
      <parameter>file:/Users/newtonm/jbossmc/microcontainer/trunk/docs/examples/User_Guide/gettingStarted/commandLineClient/target/client-cl.dir/otherLib/humanResourcesService-1.0.0.jar</parameter>
    </constructor>
  </bean>

  <bean name="customCL" class="java.net.URLClassLoader">
    <constructor>
      <parameter>
	<array>
	  <inject bean="URL"/> 
	</array>
      </parameter>
    </constructor>
  </bean>

  ...

</deployment>
			
			
			

This would be necessary to allow for reconfiguration of the service by uncommenting the AgeBasedSalary or LocationBasedSalary beans. Classloaders specified at the bean level override the deployment level classloader. To override the deployment level classloader altogether, and use the default classloader for a bean, use the <null/> value as follows:
<bean name="HRService" class="org.jboss.example.service.HRManager">
  <classloader><null/></classloader>
</bean>
		
		
		

4.5.1. Problems With Classloaders Created with Deployment Descriptors

If you create a new classloader for your service using the deployment descriptor, you may not be able to access classes loaded by it from the application classloader. In the HRManager example, the client is no longer able to cache a direct reference to the bean instance when using the Microcontainer controller.
To see this behavior, start the client using the run.sh command, then try to deploy the service. A java.lang.NoClassDefFoundError exception is thrown and the application exits.
In this scenario, you must use the bus to access the service indirectly and provide access to any classes shared by the client in the application classpath. In this example, the affected classes are Address, Employee, and SalaryStrategy.