for use with JBoss Enterprise Web Platform 5
Edition 5.1.1
Legal Notice
Abstract
- Preface
- I. Introduction to The Microcontainer - Guided Tutorial
- 1. Prerequisites to Using This Guide
- 2. Introduction to the Microcontainer
- 3. Building Services
- 4. Using Services
- 5. Adding Behavior with AOP
- II. Advanced Concepts with the Microcontainer
- A. Revision History
Mono-spaced Bold
To see the contents of the filemy_next_bestselling_novelin your current working directory, enter thecat my_next_bestselling_novelcommand at the shell prompt and press Enter to execute the command.
Press Enter to execute the command.Press Ctrl+Alt+F2 to switch to a virtual terminal.
mono-spaced bold. For example:
File-related classes includefilesystemfor file systems,filefor files, anddirfor directories. Each class has its own associated set of permissions.
Choose → → from the main menu bar to launch Mouse Preferences. In the Buttons tab, select the Left-handed mouse check box and click to switch the primary mouse button from the left to the right (making the mouse suitable for use in the left hand).To insert a special character into a gedit file, choose → → from the main menu bar. Next, choose → from the Character Map menu bar, type the name of the character in the Search field and click . The character you sought will be highlighted in the Character Table. Double-click this highlighted character to place it in the Text to copy field and then click the button. Now switch back to your document and choose → from the gedit menu bar.
Mono-spaced Bold Italic or Proportional Bold Italic
To connect to a remote machine using ssh, typesshat a shell prompt. If the remote machine isusername@domain.nameexample.comand your username on that machine is john, typessh john@example.com.Themount -o remountcommand remounts the named file system. For example, to remount thefile-system/homefile system, the command ismount -o remount /home.To see the version of a currently installed package, use therpm -qcommand. It will return a result as follows:package.package-version-release
Publican is a DocBook publishing system.
mono-spaced roman and presented thus:
books Desktop documentation drafts mss photos stuff svn books_tests Desktop1 downloads images notes scripts svgs
mono-spaced roman but add syntax highlighting as follows:
package org.jboss.book.jca.ex1;
import javax.naming.InitialContext;
public class ExClient
{
public static void main(String args[])
throws Exception
{
InitialContext iniCtx = new InitialContext();
Object ref = iniCtx.lookup("EchoBean");
EchoHome home = (EchoHome) ref;
Echo echo = home.create();
System.out.println("Created Echo");
System.out.println("Echo.echo('Hello') = " + echo.echo("Hello"));
}
}Note
Important
Warning
- search or browse through a knowledgebase of technical support articles about Red Hat products.
- submit a support case to Red Hat Global Support Services (GSS).
- access other product documentation.
JBoss Enterprise Application Platform 5 and the component doc-JBoss_Microcontainer_User_Guide. The following link will take you to a pre-filled bug report for this product: http://bugzilla.redhat.com/.
Description field. Be as specific as possible when describing the issue; this will help ensure that we can fix it quickly.
Document URL: Section Number and Name: Describe the issue: Suggestions for improvement: Additional information:
Table of Contents
- 1. Prerequisites to Using This Guide
- 2. Introduction to the Microcontainer
- 3. Building Services
- 4. Using Services
- 5. Adding Behavior with AOP
Procedure 1.1. Install Maven
Verify Java Developer Kit 1.6 or above is installed. This is also a requirement for the Enterprise Platform.
Ensure you have Java installed on your system, and have set theJAVA_HOMEenvironment variable in your~/.bash_profilefor Linux, or in the System Properties for Windows. For more information regarding setting environment variables, refer to the Step 4 step in this procedure.Download Maven
Note
This step and future steps assume that you have saved Maven to the suggested location for your operating system. Maven, as any other Java application, is able to be installed in any reasonable location on your system.Click the compiled zip archive link, for exampleapache-maven-2.2.1-bin.zipSelect a download mirror from the list.For Linux UsersSave the zip archive to yourhomedirectory.For Windows UsersSave the zip archive to yourC:\Documents and Settings\directory.user_nameInstall Maven
For Linux UsersExtract the zip file to yourhomedirectory. If you selected the zip archive in Step 2, and do not rename the directory, the extracted directory is namedapache-maven-.versionFor Windows UsersExtract the zip archive to C:\Program Files\Apache Software Foundation. If you selected the zip archive in Step 2, and do not rename the directory, the extracted directory is namedapache-maven-.versionConfigure Environment Variables
For Linux UsersAdd the following lines to your~/.bash_profile. Ensure you change the[username]to your actual username, and that the Maven directory is the actual directory name. The version number may be different than the one listed below.export M2_HOME=/home/
[username]/apache-maven-2.2.1 export M2=$M2_HOME/bin export PATH=$M2:$PATHBy includingM2at the beginning of your path, the Maven version you just installed will be the default version used. You may also want to set the path of yourJAVA_HOMEenvironment variable to the location of the JDK on your system.For Windows UsersAdd theM2_HOME,M2, andJAVA_HOMEenvironment variables.- Press Start+Pause|Break. The System Properties dialog box is displayed.
- Click the Advanced tab, then click the button.
- Under System Variables, select .
- Click , and append the two Maven paths using a semi-colon to separate each entry. Quotation marks are not required around paths.
- Add the variable
M2_HOMEand set the path toC:\Program Files\Apache Software Foundation\apache-maven-2.2.1. - Add the variable
M2and set the value to%M2_HOME%\bin.
- In the same dialog, create the
JAVA_HOMEenvironment variable:- Add the variable
%JAVA_HOME%and set the value to the location of your JDK. For exampleC:\Program Files\Java\jdk1.6.0_02.
- In the same dialog, update or create the Path environment variable:
- Add the variable
%M2%to allow Maven to be executed from the command-line. - Add the variable
%JAVA_HOME%\binto set the path to the correct Java installation.
- Click until the System Properties dialog box closes.
Implement changes to .bash_profile
For Linux Users OnlyTo update the changes made to the .bash_profile in the current terminal session, source your .bash_profile.[localhost]$ source ~/.bash_profile
Update gnome-terminal profile
For Linux Users OnlyUpdate the terminal profile to ensure that subsequent iterations of gnome-terminal (or Konsole terminal) read the new environment variables.- Click →
- Select , and click the button.
- In the Editing Profile dialog, click the Title and Command tab.
- Select the Run command as login shell check box.
- Close all open Terminal dialog boxes.
Verify the environment variable changes and Maven install
For Linux UsersTo verify that the changes have been implemented correctly, open a terminal and execute the following commands:- Execute
echo $M2_HOME, which should return the following result.[localhost]$ echo $M2_HOME /home/username/apache-maven-2.2.1
- Execute
echo $M2, which should return the following result.[localhost]$ echo $M2 /home/username/apache-maven-2.2.1/bin
- Execute
echo $PATH, and verify the Maven/bindirectory is included.[localhost]$ echo $PATH /home/username/apache-maven-2.2.1/bin
- Execute
which mvn, which should display the path to the Maven executable.[localhost]$ which mvn ~/apache-maven-2.2.1/bin/mvn
- Execute
mvn -version, which should display the Maven version, related Java version, and operating system information.[localhost]$ $ mvn -version Apache Maven 2.2.1 (r801777; 2009-08-07 05:16:01+1000) Java version: 1.6.0_0 Java home: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/jre Default locale: en_US, platform encoding: UTF-8 OS name: "Linux" version: "2.6.30.9-96.fc11.i586" arch: "i386" Family: "unix"
For Windows UsersTo verify that the changes have been implemented correctly, open a terminal and execute the following command:- In a command prompt, execute
mvn -versionC:\> mvn -version Apache Maven 2.2.1 (r801777; 2009-08-06 12:16:01-0700) Java version: 1.6.0_17 Java home: C:\Sun\SDK\jdk\jre Default locale: en_US, platform encoding: Cp1252 OS name: "windows xp" version: "5.1" arch: "x86" Family: "windows"
settings.xml File” in your ~/.m2/settings.xml (Linux) or C:\Documents and Settings\username\.m2\settings.xml (Windows). If the file does not exist, you can create it first.
Example 1.1. Example settings.xml File
<settings> <profiles> <profile> <id>jboss-public-repository</id> <repositories> <repository> <id>jboss-public-repository-group</id> <name>JBoss Public Maven Repository Group</name> <url>https://repository.jboss.org/nexus/content/groups/public/</url> <layout>default</layout> <releases> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>jboss-public-repository-group</id> <name>JBoss Public Maven Repository Group</name> <url>https://repository.jboss.org/nexus/content/groups/public/</url> <layout>default</layout> <releases> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </snapshots> </pluginRepository> </pluginRepositories> </profile> <profile> <id>jboss-deprecated-repository</id> <repositories> <repository> <id>jboss-deprecated-repository</id> <name>JBoss Deprecated Maven Repository</name> <url>https://repository.jboss.org/nexus/content/repositories/deprecated/</url> <layout>default</layout> <releases> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>never</updatePolicy> </snapshots> </repository> </repositories> </profile> </profiles> <activeProfiles> <activeProfile>jboss-public-repository</activeProfile> <activeProfile>jboss-deprecated-repository</activeProfile> </activeProfiles> </settings>
- All the features of the JMX Microkernel
- Direct POJO deployment (no need for Standard/XMBean or MBeanProxy)
- Direct IOC style dependency injection
- Improved lifecycle management
- Additional control over dependencies
- Transparent AOP integration
- Virtual File System
- Virtual Deployment Framework
- OSGi classloading
Microcontainer Definition List
- JMX Microkernel
- The JBoss JMX Microkernel is a modular Java environment. It differs from standard environments like J2EE in that the developer is able to choose exactly which components are part of the environment, and leave out the rest.
- POJO
- A Plain Old Java Object (POJO) is a modular, reusable Java object. The name is used to emphasize that a given object is an ordinary Java Object, not a special object, and in particular not an Enterprise JavaBean. The term was coined by Martin Fowler, Rebecca Parsons and Josh MacKenzie in September 2000 in a talk where they were pointing out the many benefits of encoding business logic into regular java objects rather than using Entity Beans.
- Java Bean
- A Java Bean is a reusable software component that can be manipulated visually in a builder tool.A Java Bean is an independent piece of code. It is not required to inherit from any particular base class or interface. Although Java Beans are primarily created in graphical IDEs, they can also be developed in simple text editors.
- AOP
- Aspect-Oriented Programming (AOP) is a programming paradigm in which secondary or supporting functions are isolated from the main program's business logic. It is a subset of object-oriented programming.
Note
examples/User_Guide/gettingStarted/humanResourcesService.
examples/User_Guide/gettingStarted/humanResourcesService/src/main/java/org/jboss/example/service directory, after you have extracted the ZIP file. Each of these classes represents a simple POJO that doesn't implement any special interfaces. The most important class is HRManager, which represents the service entry point providing all of the public methods that clients will call.
Methods Provided by the HRManager Class
addEmployee(Employeeemployee)removeEmployee(Employeeemployee)getEmployee(StringfirstName, StringlastName)getEmployees()getSalary(Employeeemployee)setSalary(Employeeemployee, IntegernewSalary)isHiringFreeze()setHiringFreeze(booleanhiringFreeze)getSalaryStrategy()setSalaryStrategy(SalaryStrategystrategy)
SalaryStrategy interface it is possible to configure the HRManager so that different salary strategy implementations are available to place minimum and maximum limits on the salaries for different employee roles.
mvn compile from the humanResourcesService/ directory. This creates a new directory called target/classes which contains the compiled classes. To clean up the project and remove the target directory, issue the mvn clean command.
<?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="HRService" class="org.jboss.example.service.HRManager"/> </deployment>
HRManager class and registers it with the name HRService. This file is passed to an XML deployer associated with the Microcontainer at run-time, which performs the actual deployment, and instantiates the beans.
<?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="HRService" class="org.jboss.example.service.HRManager"> <property name="salaryStrategy"><inject bean="AgeBasedSalary"/></property> </bean> <bean name="AgeBasedSalary" class="org.jboss.example.service.util.AgeBasedSalaryStrategy"/> </deployment>
HRManager created using the HRService bean. Injection is possible because the HRManager class contains a setSalaryStrategy(SalaryStrategy strategy) method. Behind the scenes, JBoss Microcontainer calls this method on the newly created HRManager instance and passes a reference to the AgeBasedSalaryStrategy instance.
HRManager hrService = new HRManager(); AgeBasedSalaryStrategy ageBasedSalary = new AgeBasedSalaryStrategy(); hrService.setSalaryStrategy(ageBasedSalary);
Employee and Address classes is unnecessary, because the client creates these in response to input from the user. They remain part of the service but are not referenced in the deployment descriptor.
- Injecting references between POJO instances
- Injecting values into POJO properties
- A hiring freeze is implemented.
- The
AgeBasedSalaryStrategyimplements new minimum and maximum salary values.
<?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="HRService" class="org.jboss.example.service.HRManager"> <property name="hiringFreeze">false</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> </deployment>
HRManager class has a setHiringFreeze(boolean hiringFreeze) method and the AgeBasedSalaryStrategy class has setMinSalary(int minSalary) and setMaxSalary(int maxSalary) methods.
MicrocontainerTest class.
org.jboss.test.kernel.junit.MicrocontainerTest class inherits from junit.framework.TestCase, setting up each test by bootstrapping JBoss Microcontainer and adding a BasicXMLDeployer. It then searches the classpath for an XML deployment descriptor with the same name as the test class, ending in .xml and residing in a directory structure representing the class's package name. Any beans found in this file are deployed and can then be accessed using a convenience method called getBean(String name).
src/test/resources Directory”.
Example 3.1. Listing of the src/test/resources Directory
├── log4j.properties
└── org
└── jboss
└── example
└── service
├── HRManagerAgeBasedTestCase.xml
├── HRManagerLocationBasedTestCase.xml
├── HRManagerTestCase.xml
└── util
├── AgeBasedSalaryTestCase.xml
└── LocationBasedSalaryTestCase.xml
Example 3.2. Listing of the src/test/java Directory
└── org
└── jboss
└── example
└── service
├── HRManagerAgeBasedTestCase.java
├── HRManagerLocationBasedTestCase.java
├── HRManagerTestCase.java
├── HRManagerTest.java
├── HRManagerTestSuite.java
└── util
├── AgeBasedSalaryTestCase.java
├── LocationBasedSalaryTestCase.java
└── SalaryStrategyTestSuite.java
HRManagerTest class extends MicrocontainerTest in order to set up a number of employees to use as the basis for the tests. Individual test cases then subclass HRManagerTest to perform the actual work. Also included are a couple of TestSuite classes that are used to group individual test cases together for convenience.
mvn test from the humanResourcesService/ directory. You should see some DEBUG log output which shows JBoss Microcontainer starting up and deploying beans from the relevant XML file before running each test. At the end of the test the beans are undeployed and the Microcontainer is shut down.
Note
HRManagerTestCase, AgeBasedSalaryTestCase, and LocationBasedSalaryTestCase, unit test individual POJOs. Other tests, such as HRManagerAgeBasedTestCase and HRManagerLocationBasedTestCase unit test entire services. Either way, the tests are run in the same manner. Using the MicrocontainerTest class makes it easy to set up and conduct comprehensive tests for any part of your code.
Address and Employee classes are not tested here. Writing tests for them is up to you.
Procedure 3.1. Packaging a Service
Place the deployment descriptor in the
META-INFdirectory (optional)If you do choose to include the deployment descriptor, by convention it should be namedjboss-beans.xmland should be placed in aMETA-INFdirectory. This is the default layout for the Enterprise Platform, so the JAR deployer recognizes this layout and automatically performs the deployment.The deployment descriptor is not included in the Human Resources example, because the service is configured by editing the descriptor directly, as a separate file.Generate the JAR
To generate a JAR containing all of the compiled classes, entermvn packagefrom thehumanResourcesService/directory.Make the JAR available to other Maven projects
To make the JAR available to other Maven projects, entermvn installin order to copy it to your local Maven repository. The final layout of the JAR is shown in Example 3.3, “Listing of theorg/jboss/example/serviceandMETA-INFDirectories”.
Example 3.3. Listing of the org/jboss/example/service and META-INFDirectories
`-- org
`-- jboss
`-- example
`-- service
|-- Address.java
|-- Employee.java
|-- HRManager.java
`-- util
|-- AgeBasedSalaryStrategy.java
|-- LocationBasedSalaryStrategy.java
`-- SalaryStrategy.java
`--META-INF
`-- MANIFEST.MF
`-- maven
`-- org.jboss.micrcontainer.examples
`-- humanResourceService
Note
META-INF/maven directory is automatically created by Maven, and will not be present if you are using a different build system.
examples/User_Guide/gettingstarted/commandLineClient directory, which follows the Maven Standard Directory Layout, as seen in Example 4.1, “Listing for examples/User_Guide/gettingstarted/commandLineClient Directory”.
Example 4.1. Listing for examples/User_Guide/gettingstarted/commandLineClient Directory
├── pom.xml
├── src
│ ├── main
│ │ ├── assembly
│ │ │ ├── aop.xml
│ │ │ ├── classloader.xml
│ │ │ ├── common.xml
│ │ │ └── pojo.xml
│ │ ├── config
│ │ │ ├── aop-beans.xml
│ │ │ ├── classloader-beans.xml
│ │ │ ├── pojo-beans.xml
│ │ │ └── run.sh
│ │ ├── java
│ │ │ └── org
│ │ │ └── jboss
│ │ │ └── example
│ │ │ └── client
│ │ │ ├── Client.java
│ │ │ ├── ConsoleInput.java
│ │ │ ├── EmbeddedBootstrap.java
│ │ │ └── UserInterface.java
│ │ └── resources
│ │ └── log4j.properties
│ └── test
│ ├── java
│ │ └── org
│ │ └── jboss
│ │ └── example
│ │ └── client
│ │ ├── ClientTestCase.java
│ │ ├── ClientTestSuite.java
│ │ └── MockUserInterface.java
│ └── resources
│ └── jboss-beans.xml
└── target
└── classes
└── log4j.properties
org/jboss/example/client directory.
UserInterface describes methods that the client calls at run-time to request user input. ConsoleInput is an implementation of UserInterface that creates a TUI which the user uses to interact with the client. The advantage of this design is that you can easily create a Swing implementation of UserInterface at a later date and replace the TUI with a GUI. You can also simulate the data-entry process with a script. Then you can check the behavior of the client automatically using conventional JUnit test cases found in Example 3.2, “Listing of the src/test/java Directory”.
auditAspect.jar from the examples/User_Guide/gettingStarted/auditAspect directory using the mvn install command. A number of different client distributions are created, including one based on AOP which relies on auditAspect.jar being available in the local Maven repository.
mvn install from the examples/User_Guide/gettingStarted directory then humanResourcesService.jar and auditAspect.jar have already been built and packaged, along with the client, so this step will not be necessary.
mvn package command from the commandLineClient directory.
Procedure 4.1. Compiling the Source Code
- Run the unit tests.
- Build a client JAR.
- Assemble a distribution containing all of the necessary files.
commandLineClient/target directory includes the subdirectories described in Example 4.2, “Subdirectories of the commandLineClient/target Directory”.
Example 4.2. Subdirectories of the commandLineClient/target Directory
client-pojo- used to call the service without AOP.
client-cl- used to demonstrate classloading features.
- client-aop
- Adding AOP support. See Chapter 5, Adding Behavior with AOP for more details.
client-pojo distribution found in the client-pojo sub-directory, which is listed in Example 4.3, “Listing of the client-pojo Directory”.
Example 4.3. Listing of the client-pojo Directory
|-- client-1.0.0.jar
|-- jboss-beans.xml
|-- lib
| |-- concurrent-1.3.4.jar
| |-- humanResourcesService-1.0.0.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
`-- run.sh
client-pojo directory and type ./run.sh. The Example 4.4, “HRManager Menu Screen” appears.
Example 4.4. HRManager Menu Screen
Menu:
d) Deploy Human Resources service
u) Undeploy Human Resources service
a) Add employee
l) List employees
r) Remove employee
g) Get a salary
s) Set a salary
t) Toggle hiring freeze
m) Display menu
p) Print service status
q) Quit
>
Important
run.sh script sets up the run-time environment by adding all of the JARs found in the lib/ directory to the classpath using the java.ext.dirs system property. It also adds the current directory and the client-1.0.0.jar using the -cp flag so that the jboss-beans.xml deployment descriptor can be found at run-time along with the org.jboss.example.client.Client class which is called to start the application.
public Client(final boolean useBus) throws Exception { this.useBus = useBus; ClassLoader cl = Thread.currentThread().getContextClassLoader(); url = cl.getResource("jboss-beans.xml"); // Start JBoss Microcontainer bootstrap = new EmbeddedBootstrap(); bootstrap.run(); kernel = bootstrap.getKernel(); controller = kernel.getController(); bus = kernel.getBus(); }
jboss-beans.xml deployment descriptor is created. This is later required so that the XML deployer can deploy and undeploy beans declared in the file. The getResource() method of the application classloader is used because the jboss-beans.xml file is included on the classpath. This is optional; the name and location of the deployment descriptor are unimportant as long as the URL is valid and reachable.
BasicBootstrap is provided as part of the Microcontainer to allow for programmatic configuration. To add an XML deployer, extend BasicBootstrap to create an EmbeddedBootstrap class and override the protected bootstrap() method as follows:
public class EmbeddedBootstrap extends BasicBootstrap { protected BasicXMLDeployer deployer; public EmbeddedBootstrap() throws Exception { super(); } public void bootstrap() throws Throwable { super.bootstrap(); deployer = new BasicXMLDeployer(getKernel()); Runtime.getRuntime().addShutdownHook(new Shutdown()); } public void deploy(URL url) { ... deployer.deploy(url); ... } public void undeploy(URL url) { ... deployer.undeploy(url); ... } protected class Shutdown extends Thread { public void run() { log.info("Shutting down"); deployer.shutdown(); } } }
shutdown hook ensures that when the JVM exits, all of the beans are undeployed in the correct order. The public deploy/undeploy methods delegate to the BasicXMLDeployer so that beans declared in jboss-beans.xml can be deployed and undeployed.
BasicXMLDeployer has parsed the jboss-beans.xml file using the URL, and instantiated the beans found within.
Note
lib/humanResourcesService.jar file. You can also place these classes in an exploded directory structure and add it to the application classpath, but packaging them in a JAR is typically more convenient.
humanResourcesService.jar file. This enables easy editing of it for testing purposes. The jboss-beans.xml file in the example contains some commented-out fragments of XML which show some of the possible configurations.
<?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="HRService" class="org.jboss.example.service.HRManager"> <!-- <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>
Important
run.sh script when the client is started, a reference to the HRService bean is looked up using the Microcontainer controller after the service is deployed:
private HRManager manager; ... private final static String HRSERVICE = "HRService"; ... void deploy() { bootstrap.deploy(url); if (!useBus && manager == null) { ControllerContext context = controller.getInstalledContext(HRSERVICE); if (context != null) { manager = (HRManager) context.getTarget(); } } }
ControllerContext, then obtains a reference to the bean instance from the context using the getTarget() method. The bean can exist within the Microcontainer in any of the states listed in States of a Bean Within the Microcontainer.
States of a Bean Within the Microcontainer
- NOT_INSTALLED
- DESCRIBED
- INSTANTIATED
- CONFIGURED
- INSTALLED
@SuppressWarnings("unchecked") Set<Employee> listEmployees() { if (useBus) ... else return manager.getEmployees(); }
jboss-beans.xml file, save the file, and deploy it again using the d option. Printing out the status of the service using the p option shows that the client is still accessing the initial instance of the service that was deployed.
Warning
run.sh script. For enterprise services such as Transactions, Messaging and Persistence this is perfectly acceptable behavior, since they are generally always in use. They cannot be redeployed at run-time and also benefit from the high performance given by using direct access. If your service falls into this category, consider using direct access via the Microcontainer controller.
run.sh script can be called with an optional parameter bus, which causes calls to the Human Resources service to use the Microcontainer bus.
invoke() method on the bus, passing in the bean name, method name, method arguments and method types. The bus uses this information to call the bean on the client's behalf.
private final static String HRSERVICE = "HRService"; ... @SuppressWarnings("unchecked") Set<Employee> listEmployees() { if (useBus) return (Set<Employee>) invoke(HRSERVICE, "getEmployees", new Object[] {}, new String[] {}); else return manager.getEmployees(); } private Object invoke(String serviceName, String methodName, Object[] args, String[] types) { Object result = null; try { result = bus.invoke(serviceName, methodName, args, types); } catch (Throwable t) { t.printStackTrace(); } return result; }
Note
jboss-beans.xml file, save the changes, and deploy it again using the d option. Print out the status again using the p option. The client is accessing the new service configuration.
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
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
java -cp `pwd`/lib/*:.:client-1.0.0.jar org.jboss.example.client.Client $1
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
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
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>
- First, create an instance of
java.net.URLcalledURL, using parameter injection in the constructor to specify the location of thehumanResourcesService.jarfile on the local file-system. - Next, create an instance of a
URLClassLoaderby injecting the URL bean into the constructor as the only element in an array. - Include a <classloader> element in your
HRServicebean definition and inject thecustomCLbean. This specifies that theHRManagerclass needs to be loaded by the customCL 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>
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>
run.sh command, then try to deploy the service. A java.lang.NoClassDefFoundError exception is thrown and the application exits.
Address, Employee, and SalaryStrategy.
HRManager class, but it would clutter the class with code which is not relevant to its central purpose, bloating it and making it harder to maintain. The design of the aspect also provide modularity, making it easy to audit other classes in the future, if the scope of the project changes.
examples/User_Guide/gettingStarted/auditAspect directory contains all the files needed to create the aspect.
pom.xmlsrc/main/java/org/jboss/example/aspect/AuditAspect.java
Example 5.1. Example POJO
public class AuditAspect { private String logDir; private BufferedWriter out; public AuditAspect() { logDir = System.getProperty("user.dir") + "/log"; File directory = new File(logDir); if (!directory.exists()) { directory.mkdir(); } } public Object audit(ConstructorInvocation inv) throws Throwable { SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyy-kkmmss"); Calendar now = Calendar.getInstance(); String filename = "auditLog-" + formatter.format(now.getTime()); File auditLog = new File(logDir + "/" + filename); auditLog.createNewFile(); out = new BufferedWriter(new FileWriter(auditLog)); return inv.invokeNext(); } public Object audit(MethodInvocation inv) throws Throwable { String name = inv.getMethod().getName(); Object[] args = inv.getArguments(); Object retVal = inv.invokeNext(); StringBuffer buffer = new StringBuffer(); for (int i=0; i < args.length; i++) { if (i > 0) { buffer.append(", "); } buffer.append(args[i].toString()); } if (out != null) { out.write("Method: " + name); if (buffer.length() > 0) { out.write(" Args: " + buffer.toString()); } if (retVal != null) { out.write(" Return: " + retVal.toString()); } out.write("\n"); out.flush(); } return retVal; } }
Procedure 5.1. Creating the POJO
- The constructor checks for the presence of a
logdirectory in the current working directory, and creates one if not found. - Next, an advice is defined. This advice is called whenever the constructor of the target class is called. This creates a new log file within the
logdirectory to record method calls made on different instances of the target class in separate files. - Finally, another advice is defined. This advice applies to each method call made on the target class.The method name and arguments are stored, along with the return value. This information is used to construct an audit record and write it to the current log file. Each advice calls
inv.invokeNext(), which chains the advices together if more than one cross-cutting concern has been applied, or to call the target constructor/method.
Note
auditAspect.jar file that can be used by other examples, type mvn install from the auditAspect directory.
lib sub-directory of the client-aop distribution located in the examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir directory:
Example 5.2. Listing of the examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir Directory
|-- client-1.0.0.jar
|-- jboss-beans.xml
|-- lib
|-- auditAspect-1.0.0.jar
|-- concurrent-1.3.4.jar
|-- humanResourcesService-1.0.0.jar
|-- javassist-3.6.0.GA.jar
|-- jboss-aop-2.0.0.beta1.jar
|-- jboss-aop-mc-int-2.0.0.Beta6.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
|-- trove-2.1.1.jar
`-- xercesImpl-2.7.1.jar
|-- log
`-- auditLog-18062010-122537
`-- run.sh
lib/auditAspect-1.0.0.jar is required to create an instance of the aspect at run-time, in order to execute the logic. Next the jar file for JBoss AOP (jboss-aop.jar), along with its dependencies javassist and trove, adds the AOP functionality. Finally, the jboss-aop-mc-int jar is required because it contains an XML schema definition that allows you to define aspects inside an XML deployment descriptor. It also contains integration code to create dependencies between normal beans and aspect beans within the Microcontainer, allowing you to add behavior during the deployment and undeployment phases.
pom.xml file and creating a valid assembly descriptor. A sample pom.xml snippet is shown in Example 5.3, “Example pom.xml Excerpt for AOP”. To perform your build using Ant, the procedure will be different.
Example 5.3. Example pom.xml Excerpt for AOP
<dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>jboss-oap</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>javassist</artifactId> <version>3.6.0.GA</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>trove</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>org.jboss.microcontainer.examples</groupId> <artifactId>jboss-aop-mc-int</artifactId> <version>2.0.0.Beta6</version> </dependency>
jboss-beans.xml to apply the audit aspect. It is in examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir.
<?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="AspectManager" class="org.jboss.aop.AspectManager"> <constructor factoryClass="org.jboss.aop.AspectManager" factoryMethod="instance"/> </bean> <aop:aspect xmlns:aop="urn:jboss:aop-beans:1.0" name="AuditAspect" class="org.jboss.example.aspect.AuditAspect" method="audit" pointcut="execution(public org.jboss.example.service.HRManager->new(..)) OR execution(public * org.jboss.example.service.HRManager->*(..))"> </aop:aspect> ... </deployment>
Procedure 5.2. Explanation of the Code to Apply an Aspect
- Before you can apply your aspect to any classes, you need to create an instance of
org.jboss.aop.AspectManagerusing a <bean> element. A factory method is used here instead of calling a conventional constructor, since only one instance of the AspectManager in the JVM is necessary at run-time. - Next an instance of our aspect called AuditAspect is created, using the <aop:aspect> element. This looks similar to the <bean> element because it has name and class attributes that are used in the same way. However it also has method and pointcut attributes that you can use to apply or bind an advice within the aspect to constructors and methods within other classes. These attributes bind the audit advice to all public constructors and methods within the
HRManagerclass. Only theauditmethod needs to be specified, since it has been overloaded within theAuditAspectclass with different parameters. JBoss AOP knows at run-time which to select, depending on whether a constructor or method invocation is being made.
run.sh script. A log directory is created on start-up alongside the lib directory when the AuditAspect bean is created by the Microcontainer. Each deployment of the Human Resources service causes a new log file to appear within the log directory. The log file contains a record of any calls made from the client to the service. It is named something similar to auditLog-28112007-163902, and contains output similar to Example 5.4, “Example AOP Log Output”.
Example 5.4. Example AOP Log Output
Method: getEmployees Return: []
Method: addEmployee Args: (Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860) Return: true
Method: getSalary Args: (Santa Claus, null - Birth date unknown) Return: 10000
Method: getEmployees Return: [(Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860)]
Method: isHiringFreeze Return: false
Method: getEmployees Return: [(Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860)]
Method: getSalaryStrategy
Warning
- NOT_INSTALLED
- the deployment descriptor containing the bean has been parsed, along with any annotations on the bean itself.
- DESCRIBED
- any dependencies created by AOP have been added to the bean, and custom annotations have been processed.
- INSTANTIATED
- an instance of the bean has been created.
- CONFIGURED
- properties have been injected into the bean, along with any references to other beans.
- CREATE
- the
createmethod, if defined in the bean, has been called. - START
- the
startmethod, if defined in the bean, has been called. - INSTALLED
- any custom install actions that were defined in the deployment descriptor have been executed and the bean is ready to access.
Important
CREATE and START states are included for legacy purposes. This allows services that were implemented as MBeans in previous versions of the Enterprise Platform to function correctly when implemented as beans in the Enterprise Platform 5.1. If you do not define any corresponding create/start methods in your bean, it will pass straight through these states.
- <aop:lifecycle-describe>
- applied when entering/leaving the DESCRIBED state
- <aop:lifecycle-instantiate>
- applied when entering/leaving the INSTANTIATED state
- <aop:lifecycle-configure>
- applied when entering/leaving the CONFIGURED state
- <aop:lifecycle-create>
- applied when entering/leaving the CREATE state
- <aop:lifecycle-start>
- applied when entering/leaving the START state
- <aop:lifecycle-install>
- applied when entering/leaving the INSTALLED state
callback class, naming it so that it can be used as beans enter or leave the relevant state during deployment and undeployment. You can specify which beans are affected by the callback using the classes attribute, as shown in Example 5.5, “Using the classes Attribute”.
Example 5.5. Using the classes Attribute
<aop:lifecycle-install xmlns:aop="urn:jboss:aop-beans:1.0" name="InstallAdvice" class="org.jboss.test.microcontainer.support.LifecycleCallback" classes="@org.jboss.test.microcontainer.support.Install"> </aop:lifecycle-install>
lifecycleCallback class is applied to any bean classes that are annotated with @org.jboss.test.microcontainer.support.Install before they enter and after they leave the INSTALLED state.
install and uninstall methods that take ControllerContext as a parameter, as shown in Example 5.6, “Install and Uninstall Methods”.
Example 5.6. Install and Uninstall Methods
import org.jboss.dependency.spi.ControllerContext; public class LifecycleCallback { public void install(ControllerContext ctx) { System.out.println("Bean " + ctx.getName() + " is being installed"; } public void uninstall(ControllerContext ctx) { System.out.println("Bean " + ctx.getName() + " is being uninstalled"; } }
install method is called during the bean's deployment, and the uninstall method during its undeployment.
Note
Example 5.7. Looking Up References To Beans
private HRManager manager; private EmbeddedBootstrap bootstrap; private Kernel kernel; private KernelController controller; private final static String HRSERVICE = "HRService"; ... // Start JBoss Microcontainer bootstrap = new EmbeddedBootstrap(); bootstrap.run(); kernel = bootstrap.getKernel(); controller = kernel.getController(); ... ControllerContext context = controller.getInstalledContext(HRSERVICE); if (context != null) { manager = (HRManager) context.getTarget(); }
Procedure 5.3. Writing Your Own ServiceLocator Implementation
- First, create an instance of JBoss NS using the Microcontainer.
- Next, add a lifecycle callback to perform the binding and unbinding of the bean references during deployment and undeployment.
- Mark the bean classes you wish to bind references for, using annotations.
- Now, you can locate the beans at run-time using the shorthand pointcut expression as shown earlier.
Example 6.1. Deployment Descriptor for a Plain POJO
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="PlainPojo" class="org.jboss.demos.models.plain.Pojo"/> <beanfactory name="PojoFactory" class="org.jboss.demos.models.plain.Pojo"> <property name="factoryClass">org.jboss.demos.models.plain.PojoFactory</property> </beanfactory> </deployment>
Example 6.2. Descriptor with Spring Support
<beans xmlns="urn:jboss:spring-beans:2.0"> <!-- Adding @Spring annotation handler --> <bean id="SpringAnnotationPlugin" class="org.jboss.spring.annotations.SpringBeanAnnotationPlugin" /> <bean id="SpringPojo" class="org.jboss.demos.models.spring.Pojo"/> </beans>
urn:jboss:spring-beans:2.0 namespace points to your version of the Spring schema port, which describes your bean's Spring style. The Microcontainer, rather than Spring's bean factory notion, deploys the beans.
Example 6.3. Using Spring with the Microcontainer
public class Pojo extends AbstractPojo implements BeanNameAware { private String beanName; public void setBeanName(String name) { beanName = name; } public String getBeanName() { return beanName; } public void start() { if ("SpringPojo".equals(getBeanName()) == false) throw new IllegalArgumentException("Name doesn't match: " + getBeanName()); } }
Example 6.4. Deployment Descriptor for Guice Integration In the Microcontainer
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="GuicePlugin" class="org.jboss.guice.spi.GuiceKernelRegistryEntryPlugin"> <constructor> <parameter> <array elementClass="com.google.inject.Module"> <bean class="org.jboss.demos.models.guice.PojoModule"/> </array> </parameter> </constructor> </bean> </deployment>
PojoModule and GuiceKernelRegistryEntryPlugin. PojoModule configures your beans, as in Example 6.5, “Configuring Beans for Guice”. GuiceKernelRegistryEntryPlugin provides integration with the Microcontainer, as shown in Example 6.6, “Guice Integration with the Microcontainer”.
Example 6.5. Configuring Beans for Guice
public class PojoModule extends AbstractModule { private Controller controller; @Constructor public PojoModule(@Inject( bean = KernelConstants.KERNEL_CONTROLLER_NAME) Controller controller) { this.controller = controller; } protected void configure() { bind(Controller.class).toInstance(controller); bind(IPojo.class).to(Pojo.class).in(Scopes.SINGLETON); bind(IPojo.class).annotatedWith(FromMC.class). toProvider(GuiceIntegration.fromMicrocontainer(IPojo.class, "PlainPojo")); } }
Example 6.6. Guice Integration with the Microcontainer
public class GuiceKernelRegistryEntryPlugin implements KernelRegistryPlugin { private Injector injector; public GuiceKernelRegistryEntryPlugin(Module... modules) { injector = Guice.createInjector(modules); } public void destroy() { injector = null; } public KernelRegistryEntry getEntry(Object name) { KernelRegistryEntry entry = null; try { if (name instanceof Class<?>) { Class<?> clazz = (Class<?>)name; entry = new AbstractKernelRegistryEntry(name, injector.getInstance(clazz)); } else if (name instanceof Key) { Key<?> key = (Key<?>)name; entry = new AbstractKernelRegistryEntry(name, injector.getInstance(key)); } } catch (Exception ignored) { } return entry; } }
Note
Injector is created from the Modules class, then does a look-up on it for matching beans. See Section 6.5, “Legacy MBeans, and Mixing Different Component Models” for information about declaring and using legacy MBeans.
Example 6.7. Injecting a POJO Into an MBean
<server> <mbean code="org.jboss.demos.models.mbeans.Pojo" name="jboss.demos:service=pojo"> <attribute name="OtherPojo"><inject bean="PlainPojo"/></attribute> </mbean> </server>
system-jmx-beans.xml for more details. The code from this file lives in the JBoss Application Server source code: system-jmx sub-project.
Example 6.8. Exposing an Existing POJO as an MBean
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AnnotatedJMXPojo" class="org.jboss.demos.models.jmx.AtJmxPojo"/> <bean name="XmlJMXPojo" class="org.jboss.demos.models.mbeans.Pojo"> <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(exposedInterface=org.jboss.demos.models.mbeans.PojoMBean.class, registerDirectly=true)</annotation> </bean> <bean name="ExposedPojo" class="org.jboss.demos.models.jmx.Pojo"/> <bean name="AnnotatedExposePojo" class="org.jboss.demos.models.jmx.ExposePojo"> <constructor> <parameter><inject bean="ExposedPojo"/></parameter> </constructor> </bean> </deployment>
org.jboss.aop.microcontainer.aspects.jmx.JMX. The bean can either be exposed directly, or in its property.
Example 6.9. Exposing a POJO as an MBean Using Its Property
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="XMLLoginConfig" class="org.jboss.demos.models.old.XMLLoginConfig"/> <bean name="SecurityConfig" class="org.jboss.demos.models.old.SecurityConfig"> <property name="defaultLoginConfig"><inject bean="XMLLoginConfig"/></property> </bean> <bean name="SecurityChecker" class="org.jboss.demos.models.old.Checker"> <property name="loginConfig"><inject bean="jboss.security:service=XMLLoginConfig"/></property> <property name="securityConfig"><inject bean="jboss.security:service=SecurityConfig"/></property> </bean> </deployment>
Example 6.10. Autowiring
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="FromGuice" class="org.jboss.demos.models.plain.FromGuice"> <constructor><parameter><inject bean="PlainPojo"/></parameter></constructor> <property name="guicePojo"><inject/></property> </bean> <bean name="AllPojos" class="org.jboss.demos.models.plain.AllPojos"> <property name="directMBean"><inject bean="jboss.demos:service=pojo"/></property> <property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property> <property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property> </bean> </deployment>
PlainPojo is injected with a common name injection. Now, you can test if Guice binding works as expected, as shown in Example 6.11, “Testing Guice Functionality”.
Example 6.11. Testing Guice Functionality
public class FromGuice { private IPojo plainPojo; private org.jboss.demos.models.guice.Pojo guicePojo; public FromGuice(IPojo plainPojo) { this.plainPojo = plainPojo; } public void setGuicePojo(org.jboss.demos.models.guice.Pojo guicePojo) { this.guicePojo = guicePojo; } public void start() { f (plainPojo != guicePojo.getMcPojo()) throw new IllegalArgumentException("Pojos are not the same: " + plainPojo + "!=" + guicePojo.getMcPojo()); } }
Example 6.12. AbstractController Source Code
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <alias name="SpringPojo">springPojo</alias> </deployment>
Example 7.1. Value Factory
<bean name="Binding" class="org.jboss.demos.ioc.vf.PortBindingManager"> <constructor> <parameter> <map keyClass="java.lang.String" valueClass="java.lang.Integer"> <entry> <key>http</key> <value>80</value> </entry> <entry> <key>ssh</key> <value>22</value> </entry> </map> </parameter> </constructor> </bean> <bean name="PortsConfig" class="org.jboss.demos.ioc.vf.PortsConfig"> <property name="http"><value-factory bean="Binding" method="getPort" parameter="http"/></property> <property name="ssh"><value-factory bean="Binding" method="getPort" parameter="ssh"/></property> <property name="ftp"> <value-factory bean="Binding" method="getPort"> <parameter>ftp</parameter> <parameter>21</parameter> </value-factory> </property> <property name="mail"> <value-factory bean="Binding" method="getPort"> <parameter>mail</parameter> <parameter>25</parameter> </value-factory> </property> </bean>
getPort method invocation.
Example 7.2. PortsConfig
public class PortBindingManager { private Map<String, Integer> bindings; public PortBindingManager(Map<String, Integer> bindings) { this.bindings = bindings; } public Integer getPort(String key) { return getPort(key, null); } public Integer getPort(String key, Integer defaultValue) { if (bindings == null) return defaultValue; Integer value = bindings.get(key); if (value != null) return value; if (defaultValue != null) bindings.put(key, defaultValue); return defaultValue; } }
Parser which collects all Editors.
Example 7.3. Callbacks to Collect and Filter Beans
<bean name="checker" class="org.jboss.demos.ioc.callback.Checker"> <constructor> <parameter> <value-factory bean="parser" method="parse"> <parameter> <array elementClass="java.lang.Object"> <value>http://www.jboss.org</value> <value>SI</value> <value>3.14</value> <value>42</value> </array> </parameter> </value-factory> </parameter> </constructor> </bean> <bean name="editorA" class="org.jboss.demos.ioc.callback.DoubleEditor"/> <bean name="editorB" class="org.jboss.demos.ioc.callback.LocaleEditor"/> <bean name="parser" class="org.jboss.demos.ioc.callback.Parser"> <incallback method="addEditor" cardinality="4..n"/> <uncallback method="removeEditor"/> </bean> <bean name="editorC" class="org.jboss.demos.ioc.callback.LongEditor"/> <bean name="editorD" class="org.jboss.demos.ioc.callback.URLEditor"/>
Example 7.4. A Parser to Collect all Editors
public class Parser { private Set<Editor> editors = new HashSet<Editor>(); ... public void addEditor(Editor editor) { editors.add(editor); } public void removeEditor(Editor editor) { editors.remove(editor); } }
incallback and uncallback use the method name for matching.
<incallback method="addEditor" cardinality="4..n"/> <uncallback method="removeEditor"/>
cardinality=4..n/>
Checker gets created and checks the parser. This is illustrated in Example 7.5, “The Checker for the Parser”.
Example 7.5. The Checker for the Parser
public void create() throws Throwable { Set<String> strings = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); for (Object element : elements) strings.add(element.toString()); if (expected.equals(strings) == false) throw new IllegalArgumentException("Illegal expected set: " + expected + "!=" + strings); }
FieldsBean Class” for an implementation.
Example 7.6. Possible BeanAccessMode Definitions
public enum BeanAccessMode { STANDARD(BeanInfoCreator.STANDARD), // Getters and Setters FIELDS(BeanInfoCreator.FIELDS), // Getters/Setters and fields without getters and setters ALL(BeanInfoCreator.ALL); // As above but with non public fields included }
Example 7.7. Setting the BeanAccessMode
<bean name="FieldsBean" class="org.jboss.demos.ioc.access.FieldsBean" access-mode="ALL"> <property name="string">InternalString</property> </bean>
Example 7.8. The FieldsBean Class
public class FieldsBean { private String string; public void start() { if (string == null) throw new IllegalArgumentException("Strings should be set!"); } }
Example 7.9. A Simple Bean Alias
<bean name="SimpleName" class="java.lang.Object"> <alias>SimpleAlias</alias> <alias replace="true">${some.system.property}</alias> <alias class="java.lang.Integer">12345</alias> <alias><javabean xmlns="urn:jboss:javabean:2.0" class="org.jboss.demos.bootstrap.Main"/></alias> </bean>
Example 7.10. Intercepting a Method based on Annotation
<interceptor xmlns="urn:jboss:aop-beans:1.0" name="StopWatchInterceptor" class="org.jboss.demos.ioc.annotations.StopWatchInterceptor"/> <bind xmlns="urn:jboss:aop-beans:1.0" pointcut="execution(* @org.jboss.demos.ioc.annotations.StopWatchLog->*(..)) OR execution(* *->@org.jboss.demos.ioc.annotations.StopWatchLog(..))"> <interceptor-ref name="StopWatchInterceptor"/> </bind> </interceptor>
public class StopWatchInterceptor implements Interceptor { ... public Object invoke(Invocation invocation) throws Throwable { Object target = invocation.getTargetObject(); long time = System.currentTimeMillis(); log.info("Invocation [" + target + "] start: " + time); try { return invocation.invokeNext(); } finally { log.info("Invocation [" + target + "] time: " + (System.currentTimeMillis() - time)); } } }
Example 7.11. A true class annotated executor
<bean name="AnnotatedExecutor" class="org.jboss.demos.ioc.annotations.AnnotatedExecutor">
public class AnnotatedExecutor implements Executor { ... @StopWatchLog // <-- Pointcut match! public void execute() throws Exception { delegate.execute(); } }
Example 7.12. Simple executor with XML annotation
<bean name="SimpleExecutor" class="org.jboss.demos.ioc.annotations.SimpleExecutor"> <annotation>@org.jboss.demos.ioc.annotations.StopWatchLog</annotation> // <-- Pointcut match! </bean>
public class SimpleExecutor implements Executor { private static Random random = new Random(); public void execute() throws Exception { Thread.sleep(Math.abs(random.nextLong() % 101)); } }
Example 7.13. Executor Logging Output
JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.AnnotatedExecutor@4d28c7] start: 1229345859234 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.AnnotatedExecutor@4d28c7] time: 31 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] start: 1229345859265 JBoss-MC-Demo INFO [15-12-2008 13:57:39] StopWatch - Invocation [org.jboss.demos.ioc.annotations.SimpleExecutor@1b044df] time: 47
Example 7.14. Include and Exclude with Autowiring
<bean name="Square" class="org.jboss.demos.ioc.autowire.Square" autowire-candidate="false"/> <bean name="Circle" class="org.jboss.demos.ioc.autowire.Circle"/> <bean name="ShapeUser" class="org.jboss.demos.ioc.autowire.ShapeUser"> <constructor> <parameter><inject/></parameter> </constructor> </bean> <bean name="ShapeHolder" class="org.jboss.demos.ioc.autowire.ShapeHolder"> <incallback method="addShape"/> <uncallback method="removeShape"/> </bean> <bean name="ShapeChecker" class="org.jboss.demos.ioc.autowire.ShapesChecker"/>
createBean method.
GenericBeanFactory instance, but you can configure your own factory. The only limitation is that its signature and configuration hooks are similar to the one of AbstractBeanFactory.
Example 7.15. Generic Bean Factory
<bean name="Object" class="java.lang.Object"/> <beanfactory name="DefaultPrototype" class="org.jboss.demos.ioc.factory.Prototype"> <property name="value"><inject bean="Object"/></property> </beanfactory> <beanfactory name="EnhancedPrototype" class="org.jboss.demos.ioc.factory.Prototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory"> <property name="value"><inject bean="Object"/></property> </beanfactory> <beanfactory name="ProxiedPrototype" class="org.jboss.demos.ioc.factory.UnmodifiablePrototype" factoryClass="org.jboss.demos.ioc.factory.EnhancedBeanFactory"> <property name="value"><inject bean="Object"/></property> </beanfactory> <bean name="PrototypeCreator" class="org.jboss.demos.ioc.factory.PrototypeCreator"> <property name="default"><inject bean="DefaultPrototype"/></property> <property name="enhanced"><inject bean="EnhancedPrototype"/></property> <property name="proxied"><inject bean="ProxiedPrototype"/></property> </bean>
Example 7.16. Extended BeanFactory
public class EnhancedBeanFactory extends GenericBeanFactory { public EnhancedBeanFactory(KernelConfigurator configurator) { super(configurator); } public Object createBean() throws Throwable { Object bean = super.createBean(); Class clazz = bean.getClass(); if (clazz.isAnnotationPresent(SetterProxy.class)) { Set<Class> interfaces = new HashSet<Class>(); addInterfaces(clazz, interfaces); return Proxy.newProxyInstance( clazz.getClassLoader(), interfaces.toArray(new Class[interfaces.size()]), new SetterInterceptor(bean) ); } else { return bean; } } protected static void addInterfaces(Class clazz, Set<Class> interfaces) { if (clazz == null) return; interfaces.addAll(Arrays.asList(clazz.getInterfaces())); addInterfaces(clazz.getSuperclass(), interfaces); } private class SetterInterceptor implements InvocationHandler { private Object target; private SetterInterceptor(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.startsWith("set")) throw new IllegalArgumentException("Cannot invoke setters."); return method.invoke(target, args); } } }
public class PrototypeCreator { ... public void create() throws Throwable { ValueInvoker vi1 = (ValueInvoker)bfDefault.createBean(); vi1.setValue("default"); ValueInvoker vi2 = (ValueInvoker)enhanced.createBean(); vi2.setValue("enhanced"); ValueInvoker vi3 = (ValueInvoker)proxied.createBean(); try { vi3.setValue("default"); throw new Error("Should not be here."); } catch (Exception ignored) { } }
BeanMetaDataBuilder to create and configure your bean metadata.
Example 7.17. BeanMetaDataBuilder
<bean name="BuilderUtil" class="org.jboss.demos.ioc.builder.BuilderUtil"/> <bean name="BuilderExampleHolder" class="org.jboss.demos.ioc.builder.BuilderExampleHolder"> <constructor> <parameter><inject bean="BUExample"/></parameter> </constructor> </bean>
public class BuilderUtil { private KernelController controller; @Constructor public BuilderUtil(@Inject(bean = KernelConstants.KERNEL_CONTROLLER_NAME) KernelController controller) { this.controller = controller; } public void create() throws Throwable { BeanMetaDataBuilder builder = BeanMetaDataBuilder.createBuilder("BUExample", BuilderExample.class.getName()); builder.addStartParameter(Kernel.class.getName(), builder.createInject(KernelConstants.KERNEL_NAME)); controller.install(builder.getBeanMetaData()); } public void destroy() { controller.uninstall("BUExample"); } }
Example 7.18. Defining a ClassLoader Per Bean
<classloader><inject bean="custom-classloader:0.0.0"/></classloader> <!-- this will be explained in future article --> <classloader name="custom-classloader" xmlns="urn:jboss:classloader:1.0" export-all="NON_EMPTY" import-all="true"/> <bean name="CustomCL" class="org.jboss.demos.ioc.classloader.CustomClassLoader"> <constructor> <parameter><inject bean="custom-classloader:0.0.0"/></parameter> </constructor> <property name="pattern">org\.jboss\.demos\.ioc\..+</property> </bean> <bean name="CB1" class="org.jboss.demos.ioc.classloader.CustomBean"/> <bean name="CB2" class="org.jboss.demos.ioc.classloader.CustomBean"> <classloader><inject bean="CustomCL"/></classloader> </bean>
Example 7.19. Custom ClassLoader Test
public class CustomClassLoader extends ClassLoader { private Pattern pattern; public CustomClassLoader(ClassLoader parent) { super(parent); } public Class<?> loadClass(String name) throws ClassNotFoundException { if (pattern == null || pattern.matcher(name).matches()) return super.loadClass(name); else throw new ClassNotFoundException("Name '" + name + "' doesn't match pattern: " + pattern); } public void setPattern(String regexp) { pattern = Pattern.compile(regexp); } }
Example 7.20. Bean Controller Mode
<bean name="OptionalService" class="org.jboss.demos.ioc.mode.OptionalService" mode="On Demand"/> <bean name="OptionalServiceUser" class="org.jboss.demos.ioc.mode.OptionalServiceUser"/> <bean name="ManualService" class="org.jboss.demos.ioc.mode.ManualService" mode="Manual"/> <bean name="ManualServiceUser" class="org.jboss.demos.ioc.mode.ManualServiceUser"> <start> <parameter><inject bean="ManualService" fromContext="context" state="Not Installed"/></parameter> </start> </bean>
Note
inject class, you can inject beans, as well as their unmodifiable Microcontainer component representation.
OptionalServiceUser and ManualServiceUser for how to use the Microcontainer API for ON_DEMAND and MANUAL bean handling.
Example 7.21. Bean Lifecycle Separation
<bean name="cycleA" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleB"/></property> </bean> <bean name="cycleB" class="org.jboss.demos.ioc.cycle.CyclePojo"> <constructor><parameter><inject bean="cycleA" state="Instantiated"/></parameter></constructor> </bean> <bean name="cycleC" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleD"/></property> </bean> <bean name="cycleD" class="org.jboss.demos.ioc.cycle.CyclePojo"> <property name="dependency"><inject bean="cycleC" state="Instantiated"/></property> </bean>
Example 7.22. Static Code Usage
<bean name="TMDemand" class="org.jboss.demos.ioc.demandsupply.TMDemander"> <demand>TM</demand> </bean> <bean name="SimpleTMSupply" class="org.jboss.demos.ioc.demandsupply.SimpleTMSupplyer"> <supply>TM</supply> </bean>
Entry invokes RepositoryManager's add and removeEntry methods to register and unregister itself.
Example 7.23. Invoking Methods in Different-States
<bean name="RepositoryManager" class="org.jboss.demos.ioc.install.RepositoryManager"> <install method="addEntry"> <parameter><inject fromContext="name"/></parameter> <parameter><this/></parameter> </install> <uninstall method="removeEntry"> <parameter><inject fromContext="name"/></parameter> </uninstall> </bean> <bean name="Entry" class="org.jboss.demos.ioc.install.SimpleEntry"> <install bean="RepositoryManager" method="addEntry" state="Instantiated"> <parameter><inject fromContext="name"/></parameter> <parameter><this/></parameter> </install> <uninstall bean="RepositoryManager" method="removeEntry" state="Configured"> <parameter><inject fromContext="name"/></parameter> </uninstall> </bean>
Example 7.24. Lazy Mock
<bean name="lazyA" class="org.jboss.demos.ioc.lazy.LazyImpl"> <constructor> <parameter> <lazy bean="lazyB"> <interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface> </lazy> </parameter> </constructor> </bean> <bean name="lazyB" class="org.jboss.demos.ioc.lazy.LazyImpl"> <constructor> <parameter> <lazy bean="lazyA"> <interface>org.jboss.demos.ioc.lazy.ILazyPojo</interface> </lazy> </parameter> </constructor> </bean> <lazy name="anotherLazy" bean="Pojo" exposeClass="true"/> <bean name="Pojo" class="org.jboss.demos.ioc.lazy.Pojo"/>
create, start, and destroy methods when it moves through the various states. However, you may not want the Microcontainer to invoke them. For this reason, an ignore flag is available.
Example 7.25. Bean Lifecycles
<bean name="FullLifecycleBean-3" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"/> <bean name="FullLifecycleBean-2" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"> <create ignored="true"/> </bean> <bean name="FullLifecycleBean-1" class="org.jboss.demos.ioc.lifecycle.FullLifecycleBean"> <start ignored="true"/> </bean>
Example 8.1. Resource Duplication Problem
public static URL[] search(ClassLoader cl, String prefix, String suffix) throws IOException { Enumeration[] e = new Enumeration[]{ cl.getResources(prefix), cl.getResources(prefix + "MANIFEST.MF") }; Set all = new LinkedHashSet(); URL url; URLConnection conn; JarFile jarFile; for (int i = 0, s = e.length; i < s; ++i) { while (e[i].hasMoreElements()) { url = (URL)e[i].nextElement(); conn = url.openConnection(); conn.setUseCaches(false); conn.setDefaultUseCaches(false); if (conn instanceof JarURLConnection) { jarFile = ((JarURLConnection)conn).getJarFile(); } else { jarFile = getAlternativeJarFile(url); } if (jarFile != null) { searchJar(cl, all, jarFile, prefix, suffix); } else { boolean searchDone = searchDir(all, new File(URLDecoder.decode(url.getFile(), "UTF-8")), suffix); if (searchDone == false) { searchFromURL(all, prefix, suffix, url); } } } } return (URL[])all.toArray(new URL[all.size()]); } private static boolean searchDir(Set result, File file, String suffix) throws IOException { if (file.exists() && file.isDirectory()) { File[] fc = file.listFiles(); String path; for (int i = 0; i < fc.length; i++) { path = fc[i].getAbsolutePath(); if (fc[i].isDirectory()) { searchDir(result, fc[i], suffix); } else if (path.endsWith(suffix)) { result.add(fc[i].toURL()); } } return true; } return false; }
Example 8.2. The VirtualFile Resource Type
public class VirtualFile implements Serializable { /** * Get certificates. * * @return the certificates associated with this virtual file */ Certificate[] getCertificates() /** * Get the simple VF name (X.java) * * @return the simple file name * @throws IllegalStateException if the file is closed */ String getName() /** * Get the VFS relative path name (org/jboss/X.java) * * @return the VFS relative path name * @throws IllegalStateException if the file is closed */ String getPathName() /** * Get the VF URL (file://root/org/jboss/X.java) * * @return the full URL to the VF in the VFS. * @throws MalformedURLException if a url cannot be parsed * @throws URISyntaxException if a uri cannot be parsed * @throws IllegalStateException if the file is closed */ URL toURL() throws MalformedURLException, URISyntaxException /** * Get the VF URI (file://root/org/jboss/X.java) * * @return the full URI to the VF in the VFS. * @throws URISyntaxException if a uri cannot be parsed * @throws IllegalStateException if the file is closed * @throws MalformedURLException for a bad url */ URI toURI() throws MalformedURLException, URISyntaxException /** * When the file was last modified * * @return the last modified time * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ long getLastModified() throws IOException /** * Returns true if the file has been modified since this method was last called * Last modified time is initialized at handler instantiation. * * @return true if modifed, false otherwise * @throws IOException for any error */ boolean hasBeenModified() throws IOException /** * Get the size * * @return the size * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ long getSize() throws IOException /** * Tests whether the underlying implementation file still exists. * @return true if the file exists, false otherwise. * @throws IOException - thrown on failure to detect existence. */ boolean exists() throws IOException /** * Whether it is a simple leaf of the VFS, * i.e. whether it can contain other files * * @return true if a simple file. * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ boolean isLeaf() throws IOException /** * Is the file archive. * * @return true if archive, false otherwise * @throws IOException for any error */ boolean isArchive() throws IOException /** * Whether it is hidden * * @return true when hidden * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ boolean isHidden() throws IOException /** * Access the file contents. * * @return an InputStream for the file contents. * @throws IOException for any error accessing the file system * @throws IllegalStateException if the file is closed */ InputStream openStream() throws IOException /** * Do file cleanup. * * e.g. delete temp files */ void cleanup() /** * Close the file resources (stream, etc.) */ void close() /** * Delete this virtual file * * @return true if file was deleted * @throws IOException if an error occurs */ boolean delete() throws IOException /** * Delete this virtual file * * @param gracePeriod max time to wait for any locks (in milliseconds) * @return true if file was deleted * @throws IOException if an error occurs */ boolean delete(int gracePeriod) throws IOException /** * Get the VFS instance for this virtual file * * @return the VFS * @throws IllegalStateException if the file is closed */ VFS getVFS() /** * Get the parent * * @return the parent or null if there is no parent * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ VirtualFile getParent() throws IOException /** * Get a child * * @param path the path * @return the child or <code>null</code> if not found * @throws IOException for any problem accessing the VFS * @throws IllegalArgumentException if the path is null * @throws IllegalStateException if the file is closed or it is a leaf node */ VirtualFile getChild(String path) throws IOException /** * Get the children * * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ List<VirtualFile> getChildren() throws IOException /** * Get the children * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed or it is a leaf node */ List<VirtualFile> getChildren(VirtualFileFilter filter) throws IOException /** * Get all the children recursively<p> * * This always uses {@link VisitorAttributes#RECURSE} * * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed */ List<VirtualFile> getChildrenRecursively() throws IOException /** * Get all the children recursively<p> * * This always uses {@link VisitorAttributes#RECURSE} * * @param filter to filter the children * @return the children * @throws IOException for any problem accessing the virtual file system * @throws IllegalStateException if the file is closed or it is a leaf node */ List<VirtualFile> getChildrenRecursively(VirtualFileFilter filter) throws IOException /** * Visit the virtual file system * * @param visitor the visitor * @throws IOException for any problem accessing the virtual file system * @throws IllegalArgumentException if the visitor is null * @throws IllegalStateException if the file is closed */ void visit(VirtualFileVisitor visitor) throws IOException }
VFS class, with the help of URL or URI parameter.
Example 8.3. Using the VFS Class
public class VFS { /** * Get the virtual file system for a root uri * * @param rootURI the root URI * @return the virtual file system * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VFS getVFS(URI rootURI) throws IOException /** * Create new root * * @param rootURI the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile createNewRoot(URI rootURI) throws IOException /** * Get the root virtual file * * @param rootURI the root uri * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VirtualFile getRoot(URI rootURI) throws IOException /** * Get the virtual file system for a root url * * @param rootURL the root url * @return the virtual file system * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL is null */ static VFS getVFS(URL rootURL) throws IOException /** * Create new root * * @param rootURL the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile createNewRoot(URL rootURL) throws IOException /** * Get the root virtual file * * @param rootURL the root url * @return the virtual file * @throws IOException if there is a problem accessing the VFS * @throws IllegalArgumentException if the rootURL */ static VirtualFile getRoot(URL rootURL) throws IOException /** * Get the root file of this VFS * * @return the root * @throws IOException for any problem accessing the VFS */ VirtualFile getRoot() throws IOException }
getVFScreateNewRootgetRoot
getVFS returns a VFS instance but does not yet create a VirtualFile instance. This is important because there are methods which help with configuring a VFS instance (see VFS class API javadocs), before instructing it to create a VirtualFile root.
createNewRoot and getRoot is in caching details, which are covered later.
Example 8.4. Using getVFS
URL rootURL = ...; // get root url VFS vfs = VFS.getVFS(rootURL); // configure vfs instance VirtualFile root1 = vfs.getRoot(); // or you can get root directly VirtualFile root2 = VFS.crateNewRoot(rootURL); VirtualFile root3 = VFS.getRoot(rootURL);
Example 8.5. Recursively Gathering Resources
public interface VirtualFileVisitor { /** * Get the search attribues for this visitor * * @return the attributes */ VisitorAttributes getAttributes(); /** * Visit a virtual file * * @param virtualFile the virtual file being visited */ void visit(VirtualFile virtualFile); } VirtualFile root = ...; // get root VirtualFileVisitor visitor = new SuffixVisitor(".class"); // get all classes root.visit(visitor);
VFSContext instance is created. This creation is done via VFSContextFactory. Different protocols map to different VFSContextFactory instances. For example, file/vfsfile maps to FileSystemContextFactory, while zip/vfszip maps to ZipEntryContextFactory.
VirtualFile instance is created, its matching VirtualFileHandler is created. This VirtualFileHandler instance knows how to handle different resource types properly; the VirtualFile API only delegates invocations to its VirtualFileHandler reference.
VFSContext instance knows how to create VirtualFileHandler instances accordingly to a resource type. For example, ZipEntryContextFactory creates ZipEntryContext, which then creates ZipEntryHandler.
Example 8.6. Implementation of Assembled VirtualFileHandlers
AssembledDirectory sar = AssembledContextFactory.getInstance().create("assembled.sar"); URL url = getResource("/vfs/test/jar1.jar"); VirtualFile jar1 = VFS.getRoot(url); sar.addChild(jar1); url = getResource("/tmp/app/ext.jar"); VirtualFile ext1 = VFS.getRoot(url); sar.addChild(ext); AssembledDirectory metainf = sar.mkdir("META-INF"); url = getResource("/config/jboss-service.xml"); VirtualFile serviceVF = VFS.getRoot(url); metainf.addChild(serviceVF); AssembledDirectory app = sar.mkdir("app.jar"); url = getResource("/app/someapp/classes"); VirtualFile appVF = VFS.getRoot(url); app.addPath(appVF, new SuffixFilter(".class"));
Example 8.7. Implementation of In-Memory VirtualFileHandlers
URL url = new URL("vfsmemory://aopdomain/org/acme/test/Test.class"); byte[] bytes = ...; // some AOP generated class bytes MemoryFileFactory.putFile(url, bytes); VirtualFile classFile = VFS.getVirtualFile(new URL("vfsmemory://aopdomain"), "org/acme/test/Test.class"); InputStream bis = classFile.openStream(); // e.g. load class from input stream
Assembled and Memory. All you need is a combination of VFSContexFactory, VFSContext, VirtualFileHandler, FileHandlerPlugin, and URLStreamHandler implementations. The VFSContextFactory is trivial, while the others depend on the complexity of your task. You could implement rar, tar, gzip, or even remote access.
gema.ear/ui.war/WEB-INF/lib/struts.jar.
struts.jar we have two options:
- handle resources in memory
- create top level temporary copies of nested jars, recursively
VFSRegistry, VFSCache, and TempInfo.
getRoot, not createNewRoot), VFS asks the VFSRegistry implementation to provide the file. The existing DefaultVFSRegistry first checks if a matching root VFSContext exists for the provided URI. If it does, DefaultVFSRegistry first tries to navigate to the existing TempInfo (link to a temporary files), falling back to regular navigation if no such temporary file exists. In this way you completely reuse any temporary files which have already been unpacked, saving time and disk space. If no matching VFSContext is found in cache, the code will create a new VFSCache entry and continue with default navigation.
VFSCache handles cached VFSContext entries depends on the implementation used. VFSCache is configurable via VFSCacheFactory. By default, nothing is cached, but there are a few useful existing VFSCache implementations, using algorithms such as Least Recently Used (LRU) or timed cache.
- classloader
- classloading
- classloading-vfs
classloader contains a custom java.lang.ClassLoader extension without any specific classloading policy. A classloading policy includes knowledge of where to load from and how to load.
Classloading is an extension of Microcontainer’s dependency mechanisms. Its VFS-backed implementation is classloading-vfs. See Chapter 8, The Virtual File System for more information on VFS.
ClassLoader implementation supports pluggable policies and is itself a final class, not meant to be altered. To write your own ClassLoader implementations, write a ClassLoaderPolicy which provides a simpler API for locating classes and resources, and for specifying other rules associated with the classloader.
ClassLoaderPolicy and register it with a ClassLoaderSystem to create a custom ClassLoader. You can also create a ClassLoaderDomain to partition the ClassLoaderSystem.
ClassLoader layer also includes the implementation of things like DelegateLoader model, classloading, resource filters, and parent-child delegation policies.
Example 9.1. ClassLoaderPolicy Class
ClassLoaderPolicy controls the way your classloading works.
public abstract class ClassLoaderPolicy extends BaseClassLoaderPolicy { public DelegateLoader getExported() public String[] getPackageNames() protected List<? extends DelegateLoader> getDelegates() protected boolean isImportAll() protected boolean isCacheable() protected boolean isBlackListable() public abstract URL getResource(String path); public InputStream getResourceAsStream(String path) public abstract void getResources(String name, Set<URL> urls) throws IOException; protected ProtectionDomain getProtectionDomain(String className, String path) public PackageInformation getPackageInformation(String packageName) public PackageInformation getClassPackageInformation(String className, String packageName) protected ClassLoader isJDKRequest(String name) } }
ClassLoaderPolicy. The first one retrieves resources based on regular expressions, while the second one handles encrypted resources.
Example 9.2. ClassLoaderPolicy with Regular Expression Support
public class RegexpClassLoaderPolicy extends ClassLoaderPolicy { private VirtualFile[] roots; private String[] packageNames; public RegexpClassLoaderPolicy(VirtualFile[] roots) { this.roots = roots; } @Override public String[] getPackageNames() { if (packageNames == null) { Set<String> exportedPackages = PackageVisitor.determineAllPackages(roots, null, ExportAll.NON_EMPTY, null, null, null); packageNames = exportedPackages.toArray(new String[exportedPackages.size()]); } return packageNames; } protected Pattern createPattern(String regexp) { boolean outside = true; StringBuilder builder = new StringBuilder(); for (int i = 0; i < regexp.length(); i++) { char ch = regexp.charAt(i); if ((ch == '[' || ch == ']' || ch == '.') && escaped(regexp, i) == false) { switch (ch) { case '[' : outside = false; break; case ']' : outside = true; break; case '.' : if (outside) builder.append("\\"); break; } } builder.append(ch); } return Pattern.compile(builder.toString()); } protected boolean escaped(String regexp, int i) { return i > 0 && regexp.charAt(i - 1) == '\\'; } public URL getResource(String path) { Pattern pattern = createPattern(path); for (VirtualFile root : roots) { URL url = findURL(root, root, pattern); if (url != null) return url; } return null; } private URL findURL(VirtualFile root, VirtualFile file, Pattern pattern) { try { String path = AbstractStructureDeployer.getRelativePath(root, file); Matcher matcher = pattern.matcher(path); if (matcher.matches()) return file.toURL(); List<VirtualFile> children = file.getChildren(); for (VirtualFile child : children) { URL url = findURL(root, child, pattern); if (url != null) return url; } return null; } catch (Exception e) { throw new RuntimeException(e); } } public void getResources(String name, Set<URL> urls) throws IOException { Pattern pattern = createPattern(name); for (VirtualFile root : roots) { RegexpVisitor visitor = new RegexpVisitor(root, pattern); root.visit(visitor); urls.addAll(visitor.getUrls()); } } private static class RegexpVisitor implements VirtualFileVisitor { private VirtualFile root; private Pattern pattern; private Set<URL> urls = new HashSet<URL>(); private RegexpVisitor(VirtualFile root, Pattern pattern) { this.root = root; this.pattern = pattern; } public VisitorAttributes getAttributes() { return VisitorAttributes.RECURSE_LEAVES_ONLY; } public void visit(VirtualFile file) { try { String path = AbstractStructureDeployer.getRelativePath(root, file); Matcher matcher = pattern.matcher(path); if (matcher.matches()) urls.add(file.toURL()); } catch (Exception e) { throw new RuntimeException(e); } } public Set<URL> getUrls() { return urls; } } }
RegexpClassLoaderPolicy uses a simplistic mechanism to find matching resources. Real-world implementations would be more comprehensive and elegant.
public class RegexpService extends PrintService { public void start() throws Exception { System.out.println(); ClassLoader cl = getClass().getClassLoader(); Enumeration<URL> urls = cl.getResources("config/[^.]+\\.[^.]{1,4}"); while (urls.hasMoreElements()) { URL url = urls.nextElement(); print(url.openStream(), url.toExternalForm()); } } }
config/[^.]+\\.[^.]{1,4} to list resources under the config// directory. The suffix length is limited, such that file names such as excluded.properties will be ignored.
Example 9.3. ClassLoaderPolicy with Encryption Support
public class CrypterClassLoaderPolicy extends VFSClassLoaderPolicy { private Crypter crypter; public CrypterClassLoaderPolicy(String name, VirtualFile[] roots, VirtualFile[] excludedRoots, Crypter crypter) { super(name, roots, excludedRoots); this.crypter = crypter; } @Override public URL getResource(String path) { try { URL resource = super.getResource(path); return wrap(resource); } catch (IOException e) { throw new RuntimeException(e); } } @Override public InputStream getResourceAsStream(String path) { InputStream stream = super.getResourceAsStream(path); return crypter.crypt(stream); } @Override public void getResources(String name, Set<URL> urls) throws IOException { super.getResources(name, urls); Set<URL> temp = new HashSet<URL>(urls.size()); for (URL url : urls) { temp.add(wrap(url)); } urls.clear(); urls.addAll(temp); } protected URL wrap(URL url) throws IOException { return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile(), new CrypterURLStreamHandler(crypter)); } }
META-INF/ directory.
public class EncryptedService extends PrintService { public void start() throws Exception { ClassLoader cl = getClass().getClassLoader(); URL url = cl.getResource("config/settings.txt"); if (url == null) throw new IllegalArgumentException("No such settings.txt."); InputStream is = url.openStream(); print(is, "Printing settings:\n"); is = cl.getResourceAsStream("config/properties.xml"); if (is == null) throw new IllegalArgumentException("No such properties.xml."); print(is, "\nPrinting properties:\n"); } }
ClassLoaderSystem and deployers.
ClassLoaderSystem is discussed later in this chapter.
ClassLoader abstraction directly, you can create ClassLoading modules which contain declarations of ClassLoader dependencies. Once the dependencies are specified the ClassLoaderPolicys are constructed and wired together accordingly.
ClassLoaders before they actually exist, the abstraction includes a ClassLoadingMetaData model.
ClassLoadingMetaData can be exposed as a Managed Object within the new JBoss EAP profile service. This helps system administrators to deal with more abstract policy details rather than the implementation details.
Example 9.4. ClassLoadingMetaData Exposed as a Managed Object
public class ClassLoadingMetaData extends NameAndVersionSupport { /** The serialVersionUID */ private static final long serialVersionUID = -2782951093046585620L; /** The classloading domain */ private String domain; /** The parent domain */ private String parentDomain; /** Whether to make a subdeployment classloader a top-level classloader */ private boolean topLevelClassLoader = false; /** Whether to enforce j2se classloading compliance */ private boolean j2seClassLoadingCompliance = true; /** Whether we are cacheable */ private boolean cacheable = true; /** Whether we are blacklistable */ private boolean blackListable = true; /** Whether to export all */ private ExportAll exportAll; /** Whether to import all */ private boolean importAll; /** The included packages */ private String includedPackages; /** The excluded packages */ private String excludedPackages; /** The excluded for export */ private String excludedExportPackages; /** The included packages */ private ClassFilter included; /** The excluded packages */ private ClassFilter excluded; /** The excluded for export */ private ClassFilter excludedExport; /** The requirements */ private RequirementsMetaData requirements = new RequirementsMetaData(); /** The capabilities */ private CapabilitiesMetaData capabilities = new CapabilitiesMetaData(); ... setters & getters
Example 9.5. ClassLoading API Defined in XML
<classloading xmlns="urn:jboss:classloading:1.0" name="ptd-jsf-1.0.war" domain="ptd-jsf-1.0.war" parent-domain="ptd-ear-1.0.ear" export-all="NON_EMPTY" import-all="true" parent-first="true"/>
Example 9.6. ClassLoading API Defined in Java
ClassLoadingMetaData clmd = new ClassLoadingMetaData(); if (name != null) clmd.setDomain(name + "_Domain"); clmd.setParentDomain(parentDomain); clmd.setImportAll(true); clmd.setExportAll(ExportAll.NON_EMPTY); clmd.setVersion(Version.DEFAULT_VERSION);
jboss-classloading.xml.
Example 9.7. Adding ClassLoadingMetaData Using jboss-classloading.xml
<classloading xmlns="urn:jboss:classloading:1.0" domain="DefaultDomain" top-level-classloader="true" export-all="NON_EMPTY" import-all="true"> </classloading>
Example 9.8. Typical Domain-Level Isolation
<classloading xmlns="urn:jboss:classloading:1.0" domain="IsolatedDomain" export-all="NON_EMPTY" import-all="true"> </classloading>
Example 9.9. Isolation with a Specific Parent
<classloading xmlns="urn:jboss:classloading:1.0" domain="IsolatedWithParentDomain" parent-domain="DefaultDomain" export-all="NON_EMPTY" import-all="true"> </classloading>
Example 9.10. Non-Compliance with j2seClassLoadingCompliance
<classloading xmlns="urn:jboss:classloading:1.0" parent-first="false"> </classloading>
.war deployments use this method by default. Instead of doing default parent-first lookups, you first check your own resources.
Example 9.11. Typical OSGi Implementation
<classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <package name="org.jboss.dependency.spi"/> </requirements> <capabilities> <package name="org.jboss.cache.api"/> <package name="org.jboss.kernel.spi"/> </capabilities> </classloading>
Example 9.12. Importing and Exporting Whole Modules and Libraries, Rather than Fine-Grained Packages
<classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <module name="jboss-reflect.jar"/> </requirements> <capabilities> <module name="jboss-cache.jar"/> </capabilities> </classloading>
<classloading xmlns="urn:jboss:classloading:1.0"> <requirements> <package name="si.acme.foobar"/> <module name="jboss-reflect.jar"/> </requirements> <capabilities> <package name="org.alesj.cl"/> <module name="jboss-cache.jar"/> </capabilities> </classloading>
ClassLoader project, the connection between deployment and classloading is done through the Module class, which holds all of the required information to properly apply restrictions on the visitor pattern, such as filtering.
Example 9.13. The ResourceVisitor and ResourceContext Interfaces
public interface ResourceVisitor { ResourceFilter getFilter(); void visit(ResourceContext resource); } public interface ResourceContext { URL getUrl(); ClassLoader getClassLoader(); String getResourceName(); String getClassName(); boolean isClass(); Class<?> loadClass(); InputStream getInputStream() throws IOException; byte[] getBytes() throws IOException; }
Module::visit method. This feature is used in the deployment framework to index annotations usage in deployments.
ClassLoaderPolicy that uses a JBoss Virtual File System project to load classes and resources. You can use this idea directly or in combination with a classloading framework.
Example 9.14. Classloading Module Deployer
<deployment xmlns="urn:jboss:bean-deployer:2.0"> <classloader name="anys-classloader" xmlns="urn:jboss:classloader:1.0" import-all="true" domain="Anys" parent-domain="DefaultDomain"> <capabilities> <package name="org.jboss.test.deployers.vfs.reflect.support.web"/> </capabilities> <root>${jboss.tests.url}</root> </classloader> <bean name="AnyServlet" class="org.jboss.test.deployers.vfs.reflect.support.web.AnyServlet"> <classloader><inject bean="anys-classloader:0.0.0"/></classloader> </bean> </deployment>
VFSClassLoaderFactory class transforms the XML deployer into a VFSClassLoaderPolicyModule, which then creates the actual ClassLoader instance. You can then directly use this new ClassLoader instance with your beans.
Note
VFSClassLoaderFactory extends ClassLoadingMetaData, so all examples pertaining to ClassLoadingMetaData apply in this case as well.
Deployment instance.
my-jboss-beans.xml, web.xml, ejb-jar.xml. Classpaths are classloader roots, such as WEB-INF/classes or myapp.ear/lib.
A Typical Deployment Lifecycle
- The
MainDeployerpasses the deployment to the set ofStructuralDeployers for recognition, and receives back a Deployment context. - Next, the
MainDeployerpasses the resulting Deployment context to theDeployersfor handling by the appropriateDeployer.
Handling StructuredMetaData Information
- VFS-based deployments
- the structure recognition is forwarded to a set of StructureDeployers.
- JEE-specification-defined structures
- we have matching StructureDeployer implementations:
- EarStructure
- WarStructure
- JarStructure
- DeclarativeStructures
- looks for
META-INF/jboss-structure.xmlfile inside your deployment, and parses it to construct a properStructureMetaData. - FileStructures
- only recognizes known configuration files, such as files like
-jboss-beans.xmlor-service.xml.Example 10.1. An example of
jboss-structure.xml<structure> <context comparator="org.jboss.test.deployment.test.SomeDeploymentComparatorTop"> <path name=""/> <metaDataPath> <path name="META-INF"/> </metaDataPath> <classpath> <path name="lib" suffixes=".jar"/> </classpath> </context> </structure>
StructureDeployer with the help of the generic GroupingStructure class provided by the generic StructureDeployer interface.
DeploymentStage.
Example 10.2. Deployment Stages
public interface DeploymentStages { /** The not installed stage - nothing is done here */ DeploymentStage NOT_INSTALLED = new DeploymentStage("Not Installed"); /** The pre parse stage - where pre parsing stuff can be prepared; altDD, ignore, ... */ DeploymentStage PRE_PARSE = new DeploymentStage("PreParse", NOT_INSTALLED); /** The parse stage - where metadata is read */ DeploymentStage PARSE = new DeploymentStage("Parse", PRE_PARSE); /** The post parse stage - where metadata can be fixed up */ DeploymentStage POST_PARSE = new DeploymentStage("PostParse", PARSE); /** The pre describe stage - where default dependencies metadata can be created */ DeploymentStage PRE_DESCRIBE = new DeploymentStage("PreDescribe", POST_PARSE); /** The describe stage - where dependencies are established */ DeploymentStage DESCRIBE = new DeploymentStage("Describe", PRE_DESCRIBE); /** The classloader stage - where classloaders are created */ DeploymentStage CLASSLOADER = new DeploymentStage("ClassLoader", DESCRIBE); /** The post classloader stage - e.g. aop */ DeploymentStage POST_CLASSLOADER = new DeploymentStage("PostClassLoader", CLASSLOADER); /** The pre real stage - where before real deployments are done */ DeploymentStage PRE_REAL = new DeploymentStage("PreReal", POST_CLASSLOADER); /** The real stage - where real deployment processing is done */ DeploymentStage REAL = new DeploymentStage("Real", PRE_REAL); /** The installed stage - could be used to provide valve in future? */ DeploymentStage INSTALLED = new DeploymentStage("Installed", REAL); }
DeploymentControllerContext. The Microcontainer's state machine handles dependencies.
parent-first property. This property is set to true by default.
all, top level, components only, or no components.
Warning
Example 10.3. Simple Deployer which Outputs Information About Its Deployment
public class StdioDeployer extends AbstractDeployer { public void deploy(DeploymentUnit unit) throws DeploymentException { System.out.println("Deploying unit: " + unit); } @Override public void undeploy(DeploymentUnit unit) { System.out.println("Undeploying unit: " + unit); } }
-jboss-beans.xml files in deployers/ directory in JBoss Application Server, and MainDeployerImpl bean will pick up this deployer via the Microcontainer's IoC callback handling.
Example 10.4. Simple Deployment Descriptor
<bean name="StdioDeployer" class="org.jboss.acme.StdioDeployer"/>
java.util.Map, whose entries each represent an attachment.
Warning
ControllerContexts inside the Microcontainer. See Why Components Map 1:1 with the ControllerContexts for the reasons behind this assertion.
Why Components Map 1:1 with the ControllerContexts
- Naming
- The component unit's name is the same as the
ControllerContext's name. - get*Scope() and get*MetaData()
- return the same MDR context that will be used by the Microcontainer for that instance.
- IncompleteDeploymentException (IDE)
- In order for the IDE to print out what dependencies are missing for a deployment, it needs to know the ControllerContext names. It discovers the name by collecting the Component DeploymentUnit's names in Component Deployers that specify them, such as
BeanMetaDataDeployeror thesetUseUnitName()method inAbstractRealDeployer.
jboss-dependency.xml configuration file in your deployments.
Example 10.5. jboss-dependency.xml
<dependency xmlns="urn:jboss:dependency:1.0"> <item whenRequired="Real" dependentState="Create">TransactionManager</item> (1) <item>my-human-readable-deployment-alias</item> (2) </dependency>
TransactionManager to be created before the deployment is in the 'Real' stage.
aliases.txt into your deployment. Each line of the file contains an alias, giving a deployment archive one or more simple names used to refer to it.
jboss-scanning.xml.
Example 10.6. jboss-scanning.xml
<scanning xmlns="urn:jboss:scanning:1.0"> <path name="myejbs.jar"> <include name="com.acme.foo"/> <exclude name="com.acme.foo.bar"/> </path> <path name="my.war/WEB-INF/classes"> <include name="com.acme.foo"/> </path> </scanning>
| Revision History | |||||
|---|---|---|---|---|---|
| Revision 5.1.1-104.400 | 2013-10-31 | ||||
| |||||
| Revision 5.1.1-104 | 2012-07-18 | ||||
| |||||
| Revision 5.1.1-100 | Mon Jul 18 2011 | ||||
| |||||
| Revision 5.1.0-100 | Mon Sep 20 2010 | ||||
| |||||