[jboss-cvs] JBossAS SVN: r100849 - projects/microcontainer/mcdocs/trunk/userguide/src/main/docbook.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Wed Feb 10 22:21:40 EST 2010
Author: scott.stark at jboss.org
Date: 2010-02-10 22:21:40 -0500 (Wed, 10 Feb 2010)
New Revision: 100849
Modified:
projects/microcontainer/mcdocs/trunk/userguide/src/main/docbook/User_Guide.xml
Log:
Add a section on customizing supply/demand matching
Modified: projects/microcontainer/mcdocs/trunk/userguide/src/main/docbook/User_Guide.xml
===================================================================
--- projects/microcontainer/mcdocs/trunk/userguide/src/main/docbook/User_Guide.xml 2010-02-11 02:34:56 UTC (rev 100848)
+++ projects/microcontainer/mcdocs/trunk/userguide/src/main/docbook/User_Guide.xml 2010-02-11 03:21:40 UTC (rev 100849)
@@ -8,48 +8,118 @@
<subtitle>User Guide</subtitle>
<issuenum> 2.0</issuenum>
<productnumber>2</productnumber>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Author_Group.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Legal_Notice.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Author_Group.xml" />
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Legal_Notice.xml" />
</bookinfo>
<preface>
<title>What this Book Covers</title>
- <para>This book aims to help you become familiar with JBoss Microcontainer in order that you can use it to develop your own services or applications.</para>
- <para>Part I 'Getting Started' introduces the technology and explains how it relates to Java development in general. It then goes on to cover a complete use-case so that you can quickly see how the microcontainer works in real-life.</para>
- <para>Part II 'POJO Development' takes a look at the various methods you can use to construct POJOs and wire them together using injection together with other features.</para>
- <para>Part III 'AOP Development' goes on to look at how you can add behaviour to your POJOs using aspects and modify the deployment/undeployment process with lifecycle callbacks.</para>
- <para> Part IV 'Extending the Microcontainer' shows how you can write your own extensions by creating new dependencies and gives some examples of how this has already been done.</para>
+ <para>This book aims to help you become familiar with JBoss Microcontainer in order that you can
+ use it to develop your own services or applications.</para>
+ <para>Part I 'Getting Started' introduces the technology and explains how it relates
+ to Java development in general. It then goes on to cover a complete use-case so that you can
+ quickly see how the microcontainer works in real-life.</para>
+ <para>Part II 'POJO Development' takes a look at the various methods you can use to
+ construct POJOs and wire them together using injection together with other features.</para>
+ <para>Part III 'AOP Development' goes on to look at how you can add behaviour to your
+ POJOs using aspects and modify the deployment/undeployment process with lifecycle
+ callbacks.</para>
+ <para> Part IV 'Extending the Microcontainer' shows how you can write your own
+ extensions by creating new dependencies and gives some examples of how this has already been
+ done.</para>
</preface>
<part>
<title>Getting Started</title>
<chapter id="overview">
<title>Introduction</title>
- <para>The Java platform has traditionally been distributed in 3 editions, each providing a different type of runtime environment: </para>
+ <para>The Java platform has traditionally been distributed in 3 editions, each providing a
+ different type of runtime environment: </para>
<itemizedlist>
<listitem>
<para>Java ME (Micro Edition) - Mobile or other embedded devices</para>
</listitem>
<listitem>
- <para>Java SE (Standard Edition) - Desktop machines or servers (typically running 2-tier applications)</para>
+ <para>Java SE (Standard Edition) - Desktop machines or servers (typically running 2-tier
+ applications)</para>
</listitem>
<listitem>
- <para>Java EE (Enterprise Edition) - Servers (typically running 3-tier applications)</para>
+ <para>Java EE (Enterprise Edition) - Servers (typically running 3-tier
+ applications)</para>
</listitem>
</itemizedlist>
- <para>Each environment aims to provide a base level of functionality on top of which developers can add their own code to create applications. For example Java SE provides networking and security libraries together with graphical user interface toolkits to facilitate the development of desktop and simple client-server applications. Java EE takes this a stage further by adding a number of 'enterprise services' such as transactions, messaging, and persistence that allow much more robust and scalable 'enterprise applications' to be developed. These services are typically combined together inside a JEE application server in order to provide a standard runtime environment for enterprise applications but it is often the case that some are never used.</para>
- <para>Services that are never used are undesirable as they can take up valuable resources such as CPU and memory resulting in lower performance. They may also clutter up the environment with redundant configuration files, complicating maintenance and adding unnecessary complexity. Given these drawbacks it would be better if there was a way to create a custom runtime environment containing only those services that were needed. You would then be able to create your own custom 'Java Edition' tailored to your requirements.</para>
- <para>JBoss Microcontainer aims to provide these capabilities by allowing services, created using Plain Old Java Objects (POJOs), to be deployed into a standard Java SE runtime environment in a controlled manner to create a customized environment for your applications. Dependencies are fully managed to ensure that new services cannot be deployed until services they depend on have first been deployed. Where it makes sense to do so you can even redeploy services at runtime providing that you access them via the microcontainer bus. Undeploying a service causes all dependent services to be undeployed first in order to maintain the integrity of the system. </para>
- <para>JBoss Application Server 5.0 uses the microcontainer to integrate enterprise services together with a Servlet/JSP container, EJB container, deployers and management utilities in order to provide a standard Java EE environment. If you need additional services then you can simply deploy these on top of Java EE to provide the functionality you need. Likewise you are free to remove any services that you don't need simply by changing the configuration. You can even use the microcontainer to do this in other environments such as Tomcat and GlassFish since you can plug in different classloading models during the service deployment phase.</para>
- <para>Since JBoss Microcontainer is very lightweight and deals with POJOs it can also be used to deploy services into a Java ME runtime environment. This opens up new possibilities for mobile applications that can now take advantage of enterprise services without requiring a full JEE application server. </para>
- <para>In common with other lightweight containers JBoss Microcontainer uses dependency injection to wire individual POJOs together to create services. Configuration is performed using either annotations or XML depending on where the information is best located. Finally unit testing is made extremely simple thanks to a helper class that extends JUnit to setup the test environment, allowing you to access POJOs and services from your test methods using just a few lines of code.</para>
+ <para>Each environment aims to provide a base level of functionality on top of which
+ developers can add their own code to create applications. For example Java SE provides
+ networking and security libraries together with graphical user interface toolkits to
+ facilitate the development of desktop and simple client-server applications. Java EE takes
+ this a stage further by adding a number of 'enterprise services' such as
+ transactions, messaging, and persistence that allow much more robust and scalable
+ 'enterprise applications' to be developed. These services are typically combined
+ together inside a JEE application server in order to provide a standard runtime environment
+ for enterprise applications but it is often the case that some are never used.</para>
+ <para>Services that are never used are undesirable as they can take up valuable resources such
+ as CPU and memory resulting in lower performance. They may also clutter up the environment
+ with redundant configuration files, complicating maintenance and adding unnecessary
+ complexity. Given these drawbacks it would be better if there was a way to create a custom
+ runtime environment containing only those services that were needed. You would then be able
+ to create your own custom 'Java Edition' tailored to your requirements.</para>
+ <para>JBoss Microcontainer aims to provide these capabilities by allowing services, created
+ using Plain Old Java Objects (POJOs), to be deployed into a standard Java SE runtime
+ environment in a controlled manner to create a customized environment for your applications.
+ Dependencies are fully managed to ensure that new services cannot be deployed until services
+ they depend on have first been deployed. Where it makes sense to do so you can even redeploy
+ services at runtime providing that you access them via the microcontainer bus. Undeploying a
+ service causes all dependent services to be undeployed first in order to maintain the
+ integrity of the system. </para>
+ <para>JBoss Application Server 5.0 uses the microcontainer to integrate enterprise services
+ together with a Servlet/JSP container, EJB container, deployers and management utilities in
+ order to provide a standard Java EE environment. If you need additional services then you
+ can simply deploy these on top of Java EE to provide the functionality you need. Likewise
+ you are free to remove any services that you don't need simply by changing the
+ configuration. You can even use the microcontainer to do this in other environments such as
+ Tomcat and GlassFish since you can plug in different classloading models during the service
+ deployment phase.</para>
+ <para>Since JBoss Microcontainer is very lightweight and deals with POJOs it can also be used
+ to deploy services into a Java ME runtime environment. This opens up new possibilities for
+ mobile applications that can now take advantage of enterprise services without requiring a
+ full JEE application server. </para>
+ <para>In common with other lightweight containers JBoss Microcontainer uses dependency
+ injection to wire individual POJOs together to create services. Configuration is performed
+ using either annotations or XML depending on where the information is best located. Finally
+ unit testing is made extremely simple thanks to a helper class that extends JUnit to setup
+ the test environment, allowing you to access POJOs and services from your test methods using
+ just a few lines of code.</para>
</chapter>
<chapter>
<title>Download and Installing</title>
- <para>JBoss Microcontainer 2.0.0 is available for download from <ulink url="http://www.jboss.org/jbossmc/downloads">the JBoss Microcontainer downloads</ulink> page. The initial downloads page is split into two sections: <ulink url="http://www.jboss.org/jbossmc/downloads/individual">Individual Projects</ulink>, which contains the jars for the individual projects that make up the JBoss Microcontainer, and <ulink url="http://www.jboss.org/jbossmc/downloads/framework">Frameworks</ulink>, which give you usable distributions containing all the dependencies. The two frameworks shipped are the <ulink url="http://www.jboss.org/jbossmc/downloads/framework/jboss-kernel">JBoss Kernel</ulink> and <ulink url="http://www.jboss.org/jbossmc/downloads/framework/jboss-deployers">JBoss Virtual Deployment Framework</ulink>. The JBoss Kernel distribution contains the core framework, which is discussed in Parts I, II and III. The JBoss Virtual Deployment Framework contains the JBoss Kernel !
as well as advanced classloading capabilities and deployer functionality, as discussed in Part IV.</para>
- <para>JBoss Microcontainer 2.0.0 is also available via Maven2. This means that you need to install and configure Maven before you can begin development. Once this is done then you can create a maven project that depends on the JBoss Microcontainer JARs in order to download and install them into your local maven repository. You can then create an assembly for your final application in order to package the relevant JARs into a distributable format.</para>
- <para>The examples created for this User Guide use Maven2 for their configuration to achieve exactly this so it is highly recommended that you <ulink url="http://labs.jboss.com/file-access/default/members/jbossmc/downloads/userGuide-examples-13112007.zip">download</ulink> these first and take a look at how they work.</para>
+ <para>JBoss Microcontainer 2.0.0 is available for download from <ulink
+ url="http://www.jboss.org/jbossmc/downloads">the JBoss Microcontainer downloads</ulink>
+ page. The initial downloads page is split into two sections: <ulink
+ url="http://www.jboss.org/jbossmc/downloads/individual">Individual Projects</ulink>, which
+ contains the jars for the individual projects that make up the JBoss Microcontainer, and
+ <ulink url="http://www.jboss.org/jbossmc/downloads/framework">Frameworks</ulink>, which
+ give you usable distributions containing all the dependencies. The two frameworks shipped
+ are the <ulink url="http://www.jboss.org/jbossmc/downloads/framework/jboss-kernel">JBoss
+ Kernel</ulink> and <ulink
+ url="http://www.jboss.org/jbossmc/downloads/framework/jboss-deployers">JBoss Virtual
+ Deployment Framework</ulink>. The JBoss Kernel distribution contains the core framework,
+ which is discussed in Parts I, II and III. The JBoss Virtual Deployment Framework contains
+ the JBoss Kernel as well as advanced classloading capabilities and deployer functionality,
+ as discussed in Part IV.</para>
+ <para>JBoss Microcontainer 2.0.0 is also available via Maven2. This means that you need to
+ install and configure Maven before you can begin development. Once this is done then you can
+ create a maven project that depends on the JBoss Microcontainer JARs in order to download
+ and install them into your local maven repository. You can then create an assembly for your
+ final application in order to package the relevant JARs into a distributable format.</para>
+ <para>The examples created for this User Guide use Maven2 for their configuration to achieve
+ exactly this so it is highly recommended that you <ulink
+ url="http://labs.jboss.com/file-access/default/members/jbossmc/downloads/userGuide-examples-13112007.zip"
+ >download</ulink> these first and take a look at how they work.</para>
<note>
- <para>To build and run the examples you first need to install and configure Maven 2.0.9 available from <ulink url="http://maven.apache.org/">http://maven.apache.org/</ulink></para>
- <para>Installation is performed by downloading and unzipping the maven-2.0.9-bin.zip file to a convenient location on your local disk. Configuration consists of adding $MAVEN_HOME/bin to your path and adding the following profile to your ~/.m2/settings.xml file:</para>
+ <para>To build and run the examples you first need to install and configure Maven 2.0.9
+ available from <ulink url="http://maven.apache.org/"
+ >http://maven.apache.org/</ulink></para>
+ <para>Installation is performed by downloading and unzipping the maven-2.0.9-bin.zip file to
+ a convenient location on your local disk. Configuration consists of adding $MAVEN_HOME/bin
+ to your path and adding the following profile to your ~/.m2/settings.xml file:</para>
<programlisting role="XML"><settings>
<profiles>
<profile>
@@ -94,81 +164,153 @@
</profile>
</profiles>
</settings></programlisting>
- <para>This profile informs maven of the two JBoss repositories (snapshots and releases) that are needed to download the JBoss Microcontainer and dependant JARs.</para>
+ <para>This profile informs maven of the two JBoss repositories (snapshots and releases) that
+ are needed to download the JBoss Microcontainer and dependant JARs.</para>
</note>
- <para>Once you have configured Maven and downloaded the examples then you can go to one of the following subdirectories in the <code>examples/User_Guide</code> directory and enter <code>mvn install</code> to perform a build:</para>
+ <para>Once you have configured Maven and downloaded the examples then you can go to one of the
+ following subdirectories in the <code>examples/User_Guide</code> directory and enter
+ <code>mvn install</code> to perform a build:</para>
<itemizedlist>
<listitem>
- <para>gettingStarted - projects for creating and using a service together with AOP</para>
+ <para>gettingStarted - projects for creating and using a service together with AOP</para>
</listitem>
<listitem>
- <para>pojoDevelopment - examples of creating and configuring POJOs using XML and annotations</para>
+ <para>pojoDevelopment - examples of creating and configuring POJOs using XML and
+ annotations</para>
</listitem>
<listitem>
<para>aopDevelopment - examples of using AOP to add behaviour to POJOs</para>
</listitem>
<listitem>
- <para>extending - examples of how we created various extensions to the microcontainer by creating new dependencies</para>
+ <para>extending - examples of how we created various extensions to the microcontainer by
+ creating new dependencies</para>
</listitem>
</itemizedlist>
- <para>Instructions on how to run the individual examples can be found in the corresponding parts of this guide.</para>
+ <para>Instructions on how to run the individual examples can be found in the corresponding
+ parts of this guide.</para>
</chapter>
<chapter>
<title>Building services</title>
- <para>Before we talk about building services using POJOs it is necessary to define what we mean by these terms.</para>
+ <para>Before we talk about building services using POJOs it is necessary to define what we
+ mean by these terms.</para>
<para><emphasis role="bold">
<emphasis role="underline">POJOs</emphasis>
</emphasis></para>
- <para>The term POJO is an acronym for Plain Old Java Object and was first coined while Rebecca Parsons, Josh MacKenzie, and Martin Fowler were preparing for a talk at a conference in September 2000. It describes the practice of encoding business logic in regular java objects instead of components such as EJB 2.1 Entity Beans. The benefit of this approach is that you're not required to implement any special interfaces. This not only keeps your code simple but allows it to be used in a wider variety of environments and makes it easy to unit test.</para>
- <para><emphasis role="bold">Definition:</emphasis> <emphasis role="italic">A POJO declares business methods, which define behaviour, and properties, which represent state. Some properties represent associations to other POJOs.</emphasis></para>
- <para>For experienced developers this should sound familiar as it mimicks almost exactly the proposals set out in the JavaBeans specification. JavaBeans describes a component model for User Interface development emphasizing simplicity and standardized naming conventions for property accessor methods. The idea was that this would allow automatic discovery of an object's properties so that an instance could easily be created and populated with state at runtime. The main use case was creating and configuring visual user interface components such as text boxes, buttons, and tables from within an Integrated Development Environment (IDE).</para>
- <para><emphasis role="bold">Definition:</emphasis> <emphasis role="italic">A Java Bean is a reusable software component that can be manipulated visually in a builder tool.</emphasis></para>
- <para> Importantly a Java Bean is not required to inherit from any particular base class or interface. Also while Java Beans are primarily targeted at builder tools they are entirely usable by programmers with conventional text editors.</para>
- <para>Strictly speaking a Java Bean should include support for events and persistence but in many cases developers choose not to implement these features and simply follow the standardized naming conventions for property accessor methods; i.e. get and set. This 'lightweight' form of Java Bean is commonly referred to as simply a 'bean' and is semantically equivalent to a POJO.</para>
- <para>The terms POJO and bean are therefore interchangeable and you will encounter both in the microcontainer documentation and configuration files.</para>
+ <para>The term POJO is an acronym for Plain Old Java Object and was first coined while Rebecca
+ Parsons, Josh MacKenzie, and Martin Fowler were preparing for a talk at a conference in
+ September 2000. It describes the practice of encoding business logic in regular java objects
+ instead of components such as EJB 2.1 Entity Beans. The benefit of this approach is that
+ you're not required to implement any special interfaces. This not only keeps your code
+ simple but allows it to be used in a wider variety of environments and makes it easy to unit
+ test.</para>
+ <para><emphasis role="bold">Definition:</emphasis>
+ <emphasis role="italic">A POJO declares business methods, which define behaviour, and
+ properties, which represent state. Some properties represent associations to other
+ POJOs.</emphasis></para>
+ <para>For experienced developers this should sound familiar as it mimicks almost exactly the
+ proposals set out in the JavaBeans specification. JavaBeans describes a component model for
+ User Interface development emphasizing simplicity and standardized naming conventions for
+ property accessor methods. The idea was that this would allow automatic discovery of an
+ object's properties so that an instance could easily be created and populated with
+ state at runtime. The main use case was creating and configuring visual user interface
+ components such as text boxes, buttons, and tables from within an Integrated Development
+ Environment (IDE).</para>
+ <para><emphasis role="bold">Definition:</emphasis>
+ <emphasis role="italic">A Java Bean is a reusable software component that can be manipulated
+ visually in a builder tool.</emphasis></para>
+ <para> Importantly a Java Bean is not required to inherit from any particular base class or
+ interface. Also while Java Beans are primarily targeted at builder tools they are entirely
+ usable by programmers with conventional text editors.</para>
+ <para>Strictly speaking a Java Bean should include support for events and persistence but in
+ many cases developers choose not to implement these features and simply follow the
+ standardized naming conventions for property accessor methods; i.e. get and set. This
+ 'lightweight' form of Java Bean is commonly referred to as simply a
+ 'bean' and is semantically equivalent to a POJO.</para>
+ <para>The terms POJO and bean are therefore interchangeable and you will encounter both in the
+ microcontainer documentation and configuration files.</para>
<para><emphasis role="bold">
<emphasis role="underline">Services</emphasis>
</emphasis></para>
- <para>The word 'service' has many definitions in the English language but in the context of developing Java applications it is helpful to define it as follows:</para>
+ <para>The word 'service' has many definitions in the English language but in the
+ context of developing Java applications it is helpful to define it as follows:</para>
<orderedlist>
<listitem>
- <para>A service should perform work that is useful to multiple clients, thereby preventing each client from having to perform the work themselves.</para>
+ <para>A service should perform work that is useful to multiple clients, thereby preventing
+ each client from having to perform the work themselves.</para>
</listitem>
<listitem>
- <para>A service should have a name that clients can lookup at runtime to gain access. This provides a standard way to access different kinds of services and removes the need for clients to explicitly create them before they can be used. </para>
+ <para>A service should have a name that clients can lookup at runtime to gain access. This
+ provides a standard way to access different kinds of services and removes the need for
+ clients to explicitly create them before they can be used. </para>
</listitem>
<listitem>
- <para>Internal changes to a service should not affect any clients. In practice this means that clients should access a service using a well defined interface so that the implementation can be changed without having to recompile any clients. </para>
+ <para>Internal changes to a service should not affect any clients. In practice this means
+ that clients should access a service using a well defined interface so that the
+ implementation can be changed without having to recompile any clients. </para>
</listitem>
</orderedlist>
- <para>Using this definition we can now answer some simple questions:</para>
+ <para>Using this definition we can now answer some simple questions:</para>
<para>Q) Is a POJO a service?</para>
- <para>A) No, because although it performs work that is useful to multiple clients you cannot access it using a name. Also clients have to explicitly create a POJO themselves either directly using the <code>new</code> operator or indirectly using a factory. </para>
- <para>Q) Does a class have to implement an interface in order to provide a 'well-defined' interface?</para>
- <para>A) Not necessarily. Providing that we don't remove fields or methods from a class, or restrict access to them, we can always change its implementation without needing to recompile any clients. See the section entitled <ulink url="http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44524">Resolution of Symbolic References</ulink> from the Java Language Specification for more details.</para>
- <para>The 'well-defined' interface in this respect is composed from the original class's fields and methods together with their access modifiers.</para>
+ <para>A) No, because although it performs work that is useful to multiple clients you cannot
+ access it using a name. Also clients have to explicitly create a POJO themselves either
+ directly using the <code>new</code> operator or indirectly using a factory. </para>
+ <para>Q) Does a class have to implement an interface in order to provide a
+ 'well-defined' interface?</para>
+ <para>A) Not necessarily. Providing that we don't remove fields or methods from a class,
+ or restrict access to them, we can always change its implementation without needing to
+ recompile any clients. See the section entitled <ulink
+ url="http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44524"
+ >Resolution of Symbolic References</ulink> from the Java Language Specification for more
+ details.</para>
+ <para>The 'well-defined' interface in this respect is composed from the original
+ class's fields and methods together with their access modifiers.</para>
<note>
- <para>Implementing an interface is only necessary if we want to allow a client to <emphasis role="bold">choose</emphasis> between <emphasis role="bold">alternative implementations</emphasis>. i.e. if the client is compiled against an interface then we can provide as many different implementations of the interface as we like without having to recompile the client. This is because the interface ensures that the method signatures do not change.</para>
+ <para>Implementing an interface is only necessary if we want to allow a client to <emphasis
+ role="bold">choose</emphasis> between <emphasis role="bold">alternative
+ implementations</emphasis>. i.e. if the client is compiled against an interface then we
+ can provide as many different implementations of the interface as we like without having
+ to recompile the client. This is because the interface ensures that the method signatures
+ do not change.</para>
</note>
- <para>What then must we do in order to create a service using a POJO? The answer is to provide a naming mechanism that allows us to register a reference to the POJO instance with a name. Clients can then lookup the POJO reference using the name at runtime and use it to perform work. The POJO class is not required to implement an interface unless it is important that the client can choose between alternative implementations. </para>
- <para>JBoss Microcontainer provides such a naming mechanism in the form of a Controller so that we can deploy our POJO services into a runtime environment such as Java SE and look them up from within our applications.</para>
- <para>Since robust implementations of Java EE services are already available from JBoss.org and other communities it is common for companies to focus on creating more 'business-oriented' services. For this reason we shall look at creating, configuring and testing a simple Human Resources service that could potentially be used by a number of different applications.</para>
+ <para>What then must we do in order to create a service using a POJO? The answer is to provide
+ a naming mechanism that allows us to register a reference to the POJO instance with a name.
+ Clients can then lookup the POJO reference using the name at runtime and use it to perform
+ work. The POJO class is not required to implement an interface unless it is important that
+ the client can choose between alternative implementations. </para>
+ <para>JBoss Microcontainer provides such a naming mechanism in the form of a Controller so
+ that we can deploy our POJO services into a runtime environment such as Java SE and look
+ them up from within our applications.</para>
+ <para>Since robust implementations of Java EE services are already available from JBoss.org
+ and other communities it is common for companies to focus on creating more
+ 'business-oriented' services. For this reason we shall look at creating,
+ configuring and testing a simple Human Resources service that could potentially be used by a
+ number of different applications.</para>
<section>
<title>Creating POJOs</title>
- <para>The example that relates to this section can be found in the <code>examples/User_Guide/gettingStarted/humanResourcesService</code> directory. The directory layout, as with all of the examples for the User Guide, follows the <ulink url="http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html">Maven Standard Directory Layout</ulink>.</para>
- <para><programlisting>humanResourcesService/pom.xml
+ <para>The example that relates to this section can be found in the
+ <code>examples/User_Guide/gettingStarted/humanResourcesService</code> directory. The
+ directory layout, as with all of the examples for the User Guide, follows the <ulink
+ url="http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html"
+ >Maven Standard Directory Layout</ulink>.</para>
+ <para>
+ <programlisting>humanResourcesService/pom.xml
/src/main/java
/test/java
- /test/resources</programlisting></para>
+ /test/resources</programlisting>
+ </para>
<para>Java source files are located in packages beneath the src/main/java directory:</para>
- <para><programlisting>org/jboss/example/service/Address.java
+ <para>
+ <programlisting>org/jboss/example/service/Address.java
/Employee.java
/HRManager.java
org/jboss/example/service/util/SalaryStrategy.java
/AgeBasedSalaryStrategy.java
- /LocationBasedSalaryStrategy.java</programlisting></para>
- <para>Each of these classes represents a simple POJO that doesn't implement any special interfaces. The most important class is HRManager as this represents the service entry point providing all of the public methods that clients will call.</para>
+ /LocationBasedSalaryStrategy.java</programlisting>
+ </para>
+ <para>Each of these classes represents a simple POJO that doesn't implement any special
+ interfaces. The most important class is HRManager as this represents the service entry
+ point providing all of the public methods that clients will call.</para>
<itemizedlist>
<listitem>
<para>addEmployee(Employee employee)</para>
@@ -201,9 +343,20 @@
<para>setSalaryStrategy(SalaryStrategy strategy)</para>
</listitem>
</itemizedlist>
- <para>The Human Resources Service is therefore composed of a handful of classes which work together to allow a list of employees, together with details of their addresses and salaries, to be maintained by an HRManger. Thanks to the SalaryStrategy interface it is possible to configure the HRManager so that different salary strategy implementations are used. These place minimum and maximum limits on the salaries that can be awarded to employees depending on various rules.</para>
- <para>To compile the source code you simply need to enter <code>mvn compile</code> from the <code>humanResourcesService/</code> directory. This will create a new directory called <code>target/classes</code> containing the compiled code. To clean up the project and remove the <code>target</code> directory simply enter <code>mvn clean</code>. </para>
- <para>Now that we have compiled our classes we need to create instances of them. This is done by creating an XML deployment descriptor that contains a list of beans representing individual instances. Each bean is given a name so that the instance can be looked up at runtime by clients.</para>
+ <para>The Human Resources Service is therefore composed of a handful of classes which work
+ together to allow a list of employees, together with details of their addresses and
+ salaries, to be maintained by an HRManger. Thanks to the SalaryStrategy interface it is
+ possible to configure the HRManager so that different salary strategy implementations are
+ used. These place minimum and maximum limits on the salaries that can be awarded to
+ employees depending on various rules.</para>
+ <para>To compile the source code you simply need to enter <code>mvn compile</code> from the
+ <code>humanResourcesService/</code> directory. This will create a new directory called
+ <code>target/classes</code> containing the compiled code. To clean up the project and
+ remove the <code>target</code> directory simply enter <code>mvn clean</code>. </para>
+ <para>Now that we have compiled our classes we need to create instances of them. This is
+ done by creating an XML deployment descriptor that contains a list of beans representing
+ individual instances. Each bean is given a name so that the instance can be looked up at
+ runtime by clients.</para>
<programlisting role="XML"><?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -213,11 +366,19 @@
<bean name="HRService" class="org.jboss.example.service.HRManager"/>
</deployment></programlisting>
- <para>Here we have declared that we want to create an instance of the HRManager class and register it with the name HRService. This file is passed to an XML deployer associated with the microcontainer at runtime to perform the actual deployment and instantiate the beans.</para>
+ <para>Here we have declared that we want to create an instance of the HRManager class and
+ register it with the name HRService. This file is passed to an XML deployer associated
+ with the microcontainer at runtime to perform the actual deployment and instantiate the
+ beans.</para>
</section>
<section>
<title>Wiring POJOs together </title>
- <para>So far we have seen how to create POJOs and declare instances of them together with names in an XML deployment descriptor. However, individual POJO instances can only provide relatively simple behaviour. Things really get interesting when we combine POJOs together to perform more complex tasks. In our example we know that we can choose different salary strategy implementations for the HRManager so how do we go about wiring the POJOs together?</para>
+ <para>So far we have seen how to create POJOs and declare instances of them together with
+ names in an XML deployment descriptor. However, individual POJO instances can only provide
+ relatively simple behaviour. Things really get interesting when we combine POJOs together
+ to perform more complex tasks. In our example we know that we can choose different salary
+ strategy implementations for the HRManager so how do we go about wiring the POJOs
+ together?</para>
<para>The answer is to use the XML deployment descriptor again as follows:</para>
<programlisting role="XML"><?xml version="1.0" encoding="UTF-8"?>
@@ -233,22 +394,51 @@
class="org.jboss.example.service.util.AgeBasedSalaryStrategy"/>
</deployment></programlisting>
- <para>We first need to create an instance of our chosen salary strategy implementation by including an additional <bean> element. Here we have chosen the AgeBasedSalaryStrategy. Next we need to inject a reference to this bean into the instance of HRManager created using the HRService bean. Injection is possible as the HRManager class contains a <code>setSalaryStrategy(SalaryStrategy strategy)</code> method. Behind the scenes JBoss Microcontainer will call this method on the newly created HRManager instance and pass in a reference to the AgeBasedSalaryStrategy instance.</para>
- <para>In other words the XML deployment descriptor causes the same sequence of events to occur as if you had written the following code:</para>
+ <para>We first need to create an instance of our chosen salary strategy implementation by
+ including an additional <bean> element. Here we have chosen the
+ AgeBasedSalaryStrategy. Next we need to inject a reference to this bean into the instance
+ of HRManager created using the HRService bean. Injection is possible as the HRManager
+ class contains a <code>setSalaryStrategy(SalaryStrategy strategy)</code> method. Behind
+ the scenes JBoss Microcontainer will call this method on the newly created HRManager
+ instance and pass in a reference to the AgeBasedSalaryStrategy instance.</para>
+ <para>In other words the XML deployment descriptor causes the same sequence of events to
+ occur as if you had written the following code:</para>
<programlisting role="JAVA">HRManager hrService = new HRManager();
AgeBasedSalaryStrategy ageBasedSalary = new AgeBasedSalaryStrategy();
hrService.setSalaryStrategy(ageBasedSalary);</programlisting>
- <para>In addition to performing injection via property setter methods JBoss Microcontainer can also perform injection via constructor parameters if necessary. For more details please see the 'Injection' chapter in Part II 'POJO Development'. </para>
+ <para>In addition to performing injection via property setter methods JBoss Microcontainer
+ can also perform injection via constructor parameters if necessary. For more details
+ please see the 'Injection' chapter in Part II 'POJO Development'. </para>
<note>
- <para>Although we can create instances of classes using the <bean> element in the deployment descriptor it is not always appropriate to do so. For example we do not need to create instances of the Employee and Address classes since these will be created by the client in response to input from the user. As such they remain part of the service but are not mentioned in the deployment descriptor. </para>
+ <para>Although we can create instances of classes using the <bean> element in the
+ deployment descriptor it is not always appropriate to do so. For example we do not need
+ to create instances of the Employee and Address classes since these will be created by
+ the client in response to input from the user. As such they remain part of the service
+ but are not mentioned in the deployment descriptor. </para>
</note>
<note>
- <para>Also note that it is possible to define multiple beans within a deployment descriptor providing that each has a unique name. The names are required in order to perform injection as shown above. However this does not mean to say that all of the beans represent services. While a service could be implemented using a single bean it is most often the case that multiple beans are used together as in our example. In these cases there is usually one bean that represents the service entry point containing the public methods intended for the clients to call. In our example this is the HRService bean. Notice that there is nothing in the XML deployment descriptor to say which beans represent a service or indeed which bean if any is the service entry point (a service may run autonomously in which case it is its own client). Care must therefore be taken when creating deployment descriptors to ensure that sufficient comments are included to describe what the beans are u!
sed for. Alternatively a naming convention such as ending each bean name that represents a service entry point with 'Service' can be used instead, e.g. HRService.</para>
+ <para>Also note that it is possible to define multiple beans within a deployment
+ descriptor providing that each has a unique name. The names are required in order to
+ perform injection as shown above. However this does not mean to say that all of the
+ beans represent services. While a service could be implemented using a single bean it is
+ most often the case that multiple beans are used together as in our example. In these
+ cases there is usually one bean that represents the service entry point containing the
+ public methods intended for the clients to call. In our example this is the HRService
+ bean. Notice that there is nothing in the XML deployment descriptor to say which beans
+ represent a service or indeed which bean if any is the service entry point (a service
+ may run autonomously in which case it is its own client). Care must therefore be taken
+ when creating deployment descriptors to ensure that sufficient comments are included to
+ describe what the beans are used for. Alternatively a naming convention such as ending
+ each bean name that represents a service entry point with 'Service' can be
+ used instead, e.g. HRService.</para>
</note>
</section>
<section>
<title>Configuring a service</title>
- <para>Injecting references between POJO instances is one way of configuring a service however we can also inject values into POJO properties. The following deployment descriptor shows how we can configure the HRManager instance to have a hiring freeze and the AgeBasedSalaryStrategy to have new minimum and maximum salary values:</para>
+ <para>Injecting references between POJO instances is one way of configuring a service
+ however we can also inject values into POJO properties. The following deployment
+ descriptor shows how we can configure the HRManager instance to have a hiring freeze and
+ the AgeBasedSalaryStrategy to have new minimum and maximum salary values:</para>
<programlisting role="XML"><?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -267,21 +457,40 @@
</bean>
</deployment></programlisting>
- <para>As with wiring POJOs together the classes need to have public setter methods for the relevant properties so that values can be injected. For example the HRManager class has a <code>setHiringFreeze(boolean hiringFreeze)</code> method and the AgeBasedSalaryStrategy class has <code>setMinSalary(int minSalary)</code> and <code>setMaxSalary(int maxSalary)</code> methods.</para>
- <para>The values in the deployment descriptor are converted from strings into the relevant types (boolean, int etc...) by JavaBean <ulink url="http://java.sun.com/j2se/1.5.0/docs/api/java/beans/PropertyEditor.html">PropertyEditors</ulink>. A large number of these are provided by default for standard types but you can easily create your own if necessary. See the Properties chapter in Part II 'POJO Development' for more details.</para>
+ <para>As with wiring POJOs together the classes need to have public setter methods for the
+ relevant properties so that values can be injected. For example the HRManager class has a
+ <code>setHiringFreeze(boolean hiringFreeze)</code> method and the AgeBasedSalaryStrategy
+ class has <code>setMinSalary(int minSalary)</code> and <code>setMaxSalary(int
+ maxSalary)</code> methods.</para>
+ <para>The values in the deployment descriptor are converted from strings into the relevant
+ types (boolean, int etc...) by JavaBean <ulink
+ url="http://java.sun.com/j2se/1.5.0/docs/api/java/beans/PropertyEditor.html"
+ >PropertyEditors</ulink>. A large number of these are provided by default for standard
+ types but you can easily create your own if necessary. See the Properties chapter in Part
+ II 'POJO Development' for more details.</para>
</section>
<section>
<title>Testing a service</title>
- <para>We should now have a good idea about how to create POJOs and configure them using an XML deployment descriptor so how do we go about testing them? Thankfully JBoss Microcontainer makes it extremely easy to unit test individual POJOs as well as POJOs that are wired together through the use of a MicrocontainerTest class.</para>
- <para>The org.jboss.test.kernel.junit.MicrocontainerTest class inherits from junit.framework.TestCase and as such it sets up each test by bootstrapping JBoss Microcontainer and adding a BasicXMLDeployer. It then looks on 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 <code>getBean(String name)</code>. </para>
- <para>You can see examples of these deployment descriptors in the src/test/resources directory:</para>
+ <para>We should now have a good idea about how to create POJOs and configure them using an
+ XML deployment descriptor so how do we go about testing them? Thankfully JBoss
+ Microcontainer makes it extremely easy to unit test individual POJOs as well as POJOs that
+ are wired together through the use of a MicrocontainerTest class.</para>
+ <para>The org.jboss.test.kernel.junit.MicrocontainerTest class inherits from
+ junit.framework.TestCase and as such it sets up each test by bootstrapping JBoss
+ Microcontainer and adding a BasicXMLDeployer. It then looks on 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
+ <code>getBean(String name)</code>. </para>
+ <para>You can see examples of these deployment descriptors in the src/test/resources
+ directory:</para>
<programlisting>org/jboss/example/service/HRManagerTestCase.xml
/HRManagerAgeBasedTestCase.xml
/HRManagerLocationBasedTestCase.xml
org/jboss/example/service/util/AgeBasedSalaryTestCase.xml
/LocationBasedSalaryTestCase.xml</programlisting>
- <para>The test code is located in the src/test/java directory:</para>
+ <para>The test code is located in the src/test/java directory:</para>
<programlisting>org/jboss/example/service/HRManagerTestCase.java
/HRManagerAgeBasedTestCase.xml
/HRManagerLocationBasedTestCase.xml
@@ -291,19 +500,43 @@
org/jboss/example/service/util/AgeBasedSalaryTestCase.java
/LocationBasedSalaryTestVase.java
/SalaryStrategyTestSuite.java</programlisting>
- <para>The 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 this to perform the actual work. You can also see a couple of TestSuite classes that are used to group individual test cases together for convenience.</para>
- <para>To run the tests simply enter <code>mvn test</code> from the <code>humanResourcesService/</code> directory. You should see some DEBUG log output which shows JBoss Microcontainer booting up and deploying beans from the relevant XML file before running each test. At the end of the test it then undeploys the beans and shuts down the microcontainer.</para>
+ <para>The 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 this to
+ perform the actual work. You can also see a couple of TestSuite classes that are used to
+ group individual test cases together for convenience.</para>
+ <para>To run the tests simply enter <code>mvn test</code> from the
+ <code>humanResourcesService/</code> directory. You should see some DEBUG log output
+ which shows JBoss Microcontainer booting up and deploying beans from the relevant XML file
+ before running each test. At the end of the test it then undeploys the beans and shuts
+ down the microcontainer.</para>
<note>
- <para>Some of the tests such as HRManagerTestCase, AgeBasedSalaryTestCase and LocationBasedSalaryTestCase simply unit test individual POJOs whilst other tests such as HRManagerAgeBasedTestCase and HRManagerLocationBasedTestCase unit test the whole service consisting of multiple POJOs wired together. Either way the method for conducting the tests remains the same. Thanks to the MicrocontainerTest class it is trivial to set up and conduct comprehensive tests for any part of your code.</para>
- <para>Finally note that we didn't unit test the Address or Employee classes here. They were deliberately left out as they only contain trivial logic which is very unlikely to break.</para>
+ <para>Some of the tests such as HRManagerTestCase, AgeBasedSalaryTestCase and
+ LocationBasedSalaryTestCase simply unit test individual POJOs whilst other tests such as
+ HRManagerAgeBasedTestCase and HRManagerLocationBasedTestCase unit test the whole service
+ consisting of multiple POJOs wired together. Either way the method for conducting the
+ tests remains the same. Thanks to the MicrocontainerTest class it is trivial to set up
+ and conduct comprehensive tests for any part of your code.</para>
+ <para>Finally note that we didn't unit test the Address or Employee classes here.
+ They were deliberately left out as they only contain trivial logic which is very
+ unlikely to break.</para>
</note>
</section>
<section>
<title>Packaging a service</title>
- <para>Now that we have successfully created and tested our service it is time to package it up so that others can use it. The simplest way to do this is to create a JAR containing all of the classes. You can choose to include the deployment descriptor if there is a sensible default way to configure the service but you are not required to do so.</para>
- <para>If you do choose to include the deployment descriptor then by convention you should call it <code>jboss-beans.xml</code> and put it in a META-INF directory. This helps if you want to later deploy the service in JBoss AS 5 as the JAR deployer recognises this layout by default and will automatically perform the deployment.</para>
- <para>In our case we will not include it as we want to configure the service by editing the descriptor directly as a separate file. To generate a JAR in the <code>target</code> directory containing all of the compiled classes simply enter <code>mvn package</code> from the <code>humanResourcesService/</code> directory. </para>
- <para>To make the JAR available to other maven projects you then need to enter <code>mvn install</code> in order to copy it to your local maven repository.</para>
+ <para>Now that we have successfully created and tested our service it is time to package it
+ up so that others can use it. The simplest way to do this is to create a JAR containing
+ all of the classes. You can choose to include the deployment descriptor if there is a
+ sensible default way to configure the service but you are not required to do so.</para>
+ <para>If you do choose to include the deployment descriptor then by convention you should
+ call it <code>jboss-beans.xml</code> and put it in a META-INF directory. This helps if you
+ want to later deploy the service in JBoss AS 5 as the JAR deployer recognises this layout
+ by default and will automatically perform the deployment.</para>
+ <para>In our case we will not include it as we want to configure the service by editing the
+ descriptor directly as a separate file. To generate a JAR in the <code>target</code>
+ directory containing all of the compiled classes simply enter <code>mvn package</code>
+ from the <code>humanResourcesService/</code> directory. </para>
+ <para>To make the JAR available to other maven projects you then need to enter <code>mvn
+ install</code> in order to copy it to your local maven repository.</para>
<para>The final layout of the JAR is as follows:</para>
<programlisting>org/jboss/example/service/Address.class
/Employee.class
@@ -314,14 +547,24 @@
META-INF/MANIFEST.MF
META-INF/maven/org.jboss.microcontainer.examples/humanResourcesService/pom.xml
/pom.properties</programlisting>
- <para>The META-INF/maven directory is automatically produced by maven and is required so that other maven projects can use this JAR as a dependency. If you are not using maven as your build system then you will not have this directory.</para>
+ <para>The META-INF/maven directory is automatically produced by maven and is required so
+ that other maven projects can use this JAR as a dependency. If you are not using maven as
+ your build system then you will not have this directory.</para>
</section>
</chapter>
<chapter>
<title>Using services </title>
- <para>In the previous chapter we looked at how to create, configure, test and package a service. It is now time to move on and create a client so that we can use the service to perform actual work.</para>
- <para>In order to keep things simple the client uses a Text User Interface (TUI) to accept input from the user and output results. In the real-world a Graphical User Interface (GUI) would almost certainly be used but here we are more concerned with demonstrating how the client interacts with the service than showing how to develop a rich desktop application.</para>
- <para>You will find all of the necessary files in the <code>examples/User_Guide/gettingstarted/commandLineClient</code> directory. As with the previous example this follows the Maven Standard Directory Layout: </para>
+ <para>In the previous chapter we looked at how to create, configure, test and package a
+ service. It is now time to move on and create a client so that we can use the service to
+ perform actual work.</para>
+ <para>In order to keep things simple the client uses a Text User Interface (TUI) to accept
+ input from the user and output results. In the real-world a Graphical User Interface (GUI)
+ would almost certainly be used but here we are more concerned with demonstrating how the
+ client interacts with the service than showing how to develop a rich desktop
+ application.</para>
+ <para>You will find all of the necessary files in the
+ <code>examples/User_Guide/gettingstarted/commandLineClient</code> directory. As with the
+ previous example this follows the Maven Standard Directory Layout: </para>
<programlisting>commandLineClient/pom.xml
/src/main/assembly
/main/config
@@ -329,21 +572,39 @@
/main/resources
/test/java
/test/resources</programlisting>
- <para>The client consists of 3 classes and 1 interface, located in the <code>src/main/java</code> directory:</para>
+ <para>The client consists of 3 classes and 1 interface, located in the
+ <code>src/main/java</code> directory:</para>
<programlisting>org/jboss/example/client/Client.java
/ConsoleInput.java
/EmbeddedBootstrap.java
/UserInterface.java</programlisting>
- <para>UserInterface describes methods that the client will call at runtime to request data from the user. ConsoleInput is an implementation of this that creates a TUI allowing the user to operate the client from the command line. The advantage of this design is that we can easily create a Swing implementation of UserInterface at a later date and replace the TUI with a GUI if we decide to improve usability. We can also create a mock implementation for testing purposes that simulates a user entering data. This allows us to check the behaviour of the client automatically using conventional JUnit test cases as demonstrated by the code in the <code>src/test/java</code> directory:</para>
+ <para>UserInterface describes methods that the client will call at runtime to request data
+ from the user. ConsoleInput is an implementation of this that creates a TUI allowing the
+ user to operate the client from the command line. The advantage of this design is that we
+ can easily create a Swing implementation of UserInterface at a later date and replace the
+ TUI with a GUI if we decide to improve usability. We can also create a mock implementation
+ for testing purposes that simulates a user entering data. This allows us to check the
+ behaviour of the client automatically using conventional JUnit test cases as demonstrated by
+ the code in the <code>src/test/java</code> directory:</para>
<programlisting>org/jboss/example/client/ClientTestCase.java
/ClientTestSuite.java
/MockUserInterface.java</programlisting>
- <para>To compile the source code, run the unit tests, build a client JAR and assemble a distribution containing all of the necessary files simply type <code>mvn package</code> from the <code>commandLineClient</code> directory. </para>
+ <para>To compile the source code, run the unit tests, build a client JAR and assemble a
+ distribution containing all of the necessary files simply type <code>mvn package</code> from
+ the <code>commandLineClient</code> directory. </para>
<warning>
- <para>For the build to work you must first have built and installed auditAspect.jar from the <code>examples/User_Guide/gettingStarted/auditAspect</code> directory using the <code>mvn install</code> command. This is because we actually create a number of different client distributions including one based on AOP which relies on auditAspect.jar being available in the local maven repositiory.</para>
- <para>If you previously typed <code>mvn install</code> from the <code>examples/User_Guide/gettingStarted</code> directory then you will have already built and installed humanResourcesService.jar together with auditAspect.jar and the client will have already been packaged so this step will not be necessary.</para>
+ <para>For the build to work you must first have built and installed auditAspect.jar from the
+ <code>examples/User_Guide/gettingStarted/auditAspect</code> directory using the
+ <code>mvn install</code> command. This is because we actually create a number of
+ different client distributions including one based on AOP which relies on auditAspect.jar
+ being available in the local maven repositiory.</para>
+ <para>If you previously typed <code>mvn install</code> from the
+ <code>examples/User_Guide/gettingStarted</code> directory then you will have already
+ built and installed humanResourcesService.jar together with auditAspect.jar and the client
+ will have already been packaged so this step will not be necessary.</para>
</warning>
- <para>Once you have successfully compiled and packaged the client you will find the following subdirectories in the <code>commandLineClient/target</code> directory:</para>
+ <para>Once you have successfully compiled and packaged the client you will find the following
+ subdirectories in the <code>commandLineClient/target</code> directory:</para>
<itemizedlist>
<listitem>
<para>client-pojo.dir - used to call the service without any AOP</para>
@@ -355,7 +616,10 @@
<para>client-aop.dir - used in Chapter 5. Adding behaviour through AOP</para>
</listitem>
</itemizedlist>
- <para>Each of these represents a different distribution containing all of the shell scripts, JARs, and XML deployment descriptors that we need to run the client in different configurations. For the moment we will use the client-pojo distribution which can be found in the client-pojo.dir subdirectory:</para>
+ <para>Each of these represents a different distribution containing all of the shell scripts,
+ JARs, and XML deployment descriptors that we need to run the client in different
+ configurations. For the moment we will use the client-pojo distribution which can be found
+ in the client-pojo.dir subdirectory:</para>
<programlisting>run.sh
client-1.0.0.jar
jboss-beans.xml
@@ -371,19 +635,30 @@
/jbossxb-2.0.0.CR4.jar
/log4j-1.2.14.jar
/xercesImpl-2.7.1.jar</programlisting>
- <para>To run the client simply <code>cd</code> into client-pojo.dir and type <code>./run.sh</code>. You will then be presented with the following menu of options: </para>
+ <para>To run the client simply <code>cd</code> into client-pojo.dir and type
+ <code>./run.sh</code>. You will then be presented with the following menu of options: </para>
<mediaobject>
<imageobject>
- <imagedata align="center" fileref="images/tuiMenu.png"/>
+ <imagedata align="center" fileref="images/tuiMenu.png" />
</imageobject>
</mediaobject>
- <para>To select an option enter the letter shown on the left-hand side and press return. For example to redisplay the menu options enter 'm' followed by return. This is useful if they scroll off the top of the screen as you input data and read results. Entering more than one letter or entering an invalid option will result in an appropriate error message.</para>
+ <para>To select an option enter the letter shown on the left-hand side and press return. For
+ example to redisplay the menu options enter 'm' followed by return. This is useful
+ if they scroll off the top of the screen as you input data and read results. Entering more
+ than one letter or entering an invalid option will result in an appropriate error
+ message.</para>
<important>
- <para>The run.sh script sets up the runtime environment by adding all of the JARs found in the <code>lib</code> directory to the classpath using the <code>java.ext.dirs</code> system property. It also adds the current directory and the client-1.0.0.jar using the <code>-cp</code> flag so that the jboss-beans.xml deployment descriptor can be found at runtime together with the org.jboss.example.client.Client class which is called to start the application.</para>
+ <para>The run.sh script sets up the runtime environment by adding all of the JARs found in
+ the <code>lib</code> directory to the classpath using the <code>java.ext.dirs</code>
+ system property. It also adds the current directory and the client-1.0.0.jar using the
+ <code>-cp</code> flag so that the jboss-beans.xml deployment descriptor can be found at
+ runtime together with the org.jboss.example.client.Client class which is called to start
+ the application.</para>
</important>
<section>
<title>Bootstrapping the microcontainer</title>
- <para>Before we use the client to deploy and call our service, lets take a closer look at what happened during its construction:</para>
+ <para>Before we use the client to deploy and call our service, lets take a closer look at
+ what happened during its construction:</para>
<programlisting role="JAVA">public Client(final boolean useBus) throws Exception {
this.useBus = useBus;
@@ -398,8 +673,18 @@
controller = kernel.getController();
bus = kernel.getBus();
}</programlisting>
- <para>First of all a url representing the jboss-beans.xml deployment descriptor was created. This is required later on by the XML deployer so that we can deploy and undeploy beans declared in the file. Here we have chosen to use the getResource() method of the application classloader as we deliberately placed the jboss-beans.xml file on the classpath. However, this is not a requirement and you are free to call the deployment descriptor anything you like and place it anywhere you want provided that the URL is valid and reachable.</para>
- <para>Next we created an instance of JBoss Microcontainer together with an XML deployer. This process is called bootstrapping and for convenience a BasicBootstrap class is provided as part of the microcontainer to allow for configuration via system properties. To add an XML deployer we simply extend BasicBootstrap to create an EmbeddedBootstrap class and override the protected bootstrap() method as follows:</para>
+ <para>First of all a url representing the jboss-beans.xml deployment descriptor was created.
+ This is required later on by the XML deployer so that we can deploy and undeploy beans
+ declared in the file. Here we have chosen to use the getResource() method of the
+ application classloader as we deliberately placed the jboss-beans.xml file on the
+ classpath. However, this is not a requirement and you are free to call the deployment
+ descriptor anything you like and place it anywhere you want provided that the URL is valid
+ and reachable.</para>
+ <para>Next we created an instance of JBoss Microcontainer together with an XML deployer.
+ This process is called bootstrapping and for convenience a BasicBootstrap class is
+ provided as part of the microcontainer to allow for configuration via system properties.
+ To add an XML deployer we simply extend BasicBootstrap to create an EmbeddedBootstrap
+ class and override the protected bootstrap() method as follows:</para>
<programlisting role="JAVA">public class EmbeddedBootstrap extends BasicBootstrap {
protected BasicXMLDeployer deployer;
@@ -433,23 +718,37 @@
}
}
}</programlisting>
- <para>The shutdown hook ensures that when the JVM is exited all of the beans are undeployed in the correct order. The public deploy/undeploy methods simply delegate to the BasicXMLDeployer so that we can deploy and undeploy beans declared in our jboss-beans.xml file.</para>
- <para>Finally we stored references to the microcontainer controller and bus so that we can lookup bean references by name and access them directly or indirectly as necessary.</para>
+ <para>The shutdown hook ensures that when the JVM is exited all of the beans are undeployed
+ in the correct order. The public deploy/undeploy methods simply delegate to the
+ BasicXMLDeployer so that we can deploy and undeploy beans declared in our jboss-beans.xml
+ file.</para>
+ <para>Finally we stored references to the microcontainer controller and bus so that we can
+ lookup bean references by name and access them directly or indirectly as necessary.</para>
</section>
<section>
<title>Deploying the service</title>
- <para>Once the client has been created we can proceed to deploy the Human Resources service. This is done by entering the 'd' option. You should see the following log output indicating that the BasicXMLDeployer has parsed the <emphasis>
+ <para>Once the client has been created we can proceed to deploy the Human Resources service.
+ This is done by entering the 'd' option. You should see the following log output
+ indicating that the BasicXMLDeployer has parsed the <emphasis>
<property>jboss-beans.xml</property>
</emphasis> file using the url and instantiated the beans found within.</para>
<mediaobject>
<imageobject>
- <imagedata align="center" fileref="images/deployOutput.png"/>
+ <imagedata align="center" fileref="images/deployOutput.png" />
</imageobject>
</mediaobject>
<note>
- <para>The microcontainer is able to instantiate the beans as their classes are available in the extension classpath inside the <code>lib/humanResourcesService.jar</code> file. We could have equally placed these classes in an exploded directory structure and added this to the application classpath but in general you will package them in a JAR for convenience. </para>
+ <para>The microcontainer is able to instantiate the beans as their classes are available
+ in the extension classpath inside the <code>lib/humanResourcesService.jar</code> file.
+ We could have equally placed these classes in an exploded directory structure and added
+ this to the application classpath but in general you will package them in a JAR for
+ convenience. </para>
</note>
- <para>Notice that our deployment descriptor is entirely separate from the humanResourcesService.jar file. This is done on purpose so that you can easily edit it and test your changes by redeploying the service. The jboss-beans.xml file in the example contains some commented out fragments of XML that you can uncomment to configure it in different ways:</para>
+ <para>Notice that our deployment descriptor is entirely separate from the
+ humanResourcesService.jar file. This is done on purpose so that you can easily edit it and
+ test your changes by redeploying the service. The jboss-beans.xml file in the example
+ contains some commented out fragments of XML that you can uncomment to configure it in
+ different ways:</para>
<programlisting role="XML"><?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"
@@ -474,12 +773,20 @@
</deployment></programlisting>
<important>
- <para>Depending on how you access the service at runtime you may need to shutdown the application and restart it again to redeploy the service and see your changes. This reduces the flexibility of the application but results in faster performance at runtime. Alternatively you may be able to simply redeploy the service whilst the application is running. This increases flexilibity but results in slower runtime performance. Careful consideration of these tradeoffs must be made before deciding on the approach you will take.</para>
+ <para>Depending on how you access the service at runtime you may need to shutdown the
+ application and restart it again to redeploy the service and see your changes. This
+ reduces the flexibility of the application but results in faster performance at runtime.
+ Alternatively you may be able to simply redeploy the service whilst the application is
+ running. This increases flexilibity but results in slower runtime performance. Careful
+ consideration of these tradeoffs must be made before deciding on the approach you will
+ take.</para>
</important>
</section>
<section>
<title>Direct access</title>
- <para>If no parameter is given to the <code>run.sh</code> script when the client is started then a reference to the HRService bean is looked up using the microcontainer controller once the service is deployed:</para>
+ <para>If no parameter is given to the <code>run.sh</code> script when the client is started
+ then a reference to the HRService bean is looked up using the microcontainer controller
+ once the service is deployed:</para>
<programlisting role="JAVA">private HRManager manager;
...
private final static String HRSERVICE = "HRService";
@@ -493,8 +800,17 @@
if (context != null) { manager = (HRManager) context.getTarget(); }
}
}</programlisting>
- <para>Rather than immediately looking up a reference to the bean instance we first lookup a reference to a <code>ControllerContext</code>. We then obtain a reference to the bean instance from the context using the <code>getTarget()</code> method. The reason for this is because the bean can exist in many states within the microcontainer e.g. NOT_INSTALLED, DESCRIBED, INSTANTIATED, CONFIGURED, INSTALLED. In order to keep track of which state the bean is in we need to wrap it in another object called a context that describes the current state. The name of the context is the same as the bean name. Once a context reaches the INSTALLED state then the bean it represents is considered to be deployed.</para>
- <para>Now that we have a reference to the bean instance representing our service entry point we can call methods on it to perform work:</para>
+ <para>Rather than immediately looking up a reference to the bean instance we first lookup a
+ reference to a <code>ControllerContext</code>. We then obtain a reference to the bean
+ instance from the context using the <code>getTarget()</code> method. The reason for this
+ is because the bean can exist in many states within the microcontainer e.g. NOT_INSTALLED,
+ DESCRIBED, INSTANTIATED, CONFIGURED, INSTALLED. In order to keep track of which state the
+ bean is in we need to wrap it in another object called a context that describes the
+ current state. The name of the context is the same as the bean name. Once a context
+ reaches the INSTALLED state then the bean it represents is considered to be
+ deployed.</para>
+ <para>Now that we have a reference to the bean instance representing our service entry point
+ we can call methods on it to perform work:</para>
<programlisting role="JAVA">@SuppressWarnings("unchecked")
Set<Employee> listEmployees() {
if (useBus)
@@ -502,22 +818,61 @@
else
return manager.getEmployees();
}</programlisting>
- <para>The client is said to be accessing the service directly since it's using a reference to the actual bean instance. Performance is good as each method call goes directly to the bean but what happens if we want to reconfigure the service and redeploy it whilst the application is running? </para>
- <para>Reconfiguration as we know is achieved by making changes to the XML deployment descriptor and saving the file but in order to redeploy the service we must first undeploy the current one. During undeployment the microcontainer controller releases its reference to the bean instance together with any beans that depend on it. The intention is that these beans will subsequently become available for garbage collection as they are no longer required by the application. Deploying the service again creates new bean instances representing the new configuration. Any subsequent lookups from clients will then retrieve references to these new instances and they will be able to access the reconfigured service.</para>
- <para>The problem is that we cache the reference to the bean instance representing our service entry point in our client when we deploy the service for the first time. Undeploying the service therefore has no affect as the bean instance can still be accessed using the cached reference and it will not be garbage collected until the client releases it. Similarly deploying the service again will not cause another lookup as the client already has a cached reference. It will therefore continue to use the bean instance representing the initial service configuration.</para>
+ <para>The client is said to be accessing the service directly since it's using a
+ reference to the actual bean instance. Performance is good as each method call goes
+ directly to the bean but what happens if we want to reconfigure the service and redeploy
+ it whilst the application is running? </para>
+ <para>Reconfiguration as we know is achieved by making changes to the XML deployment
+ descriptor and saving the file but in order to redeploy the service we must first undeploy
+ the current one. During undeployment the microcontainer controller releases its reference
+ to the bean instance together with any beans that depend on it. The intention is that
+ these beans will subsequently become available for garbage collection as they are no
+ longer required by the application. Deploying the service again creates new bean instances
+ representing the new configuration. Any subsequent lookups from clients will then retrieve
+ references to these new instances and they will be able to access the reconfigured
+ service.</para>
+ <para>The problem is that we cache the reference to the bean instance representing our
+ service entry point in our client when we deploy the service for the first time.
+ Undeploying the service therefore has no affect as the bean instance can still be accessed
+ using the cached reference and it will not be garbage collected until the client releases
+ it. Similarly deploying the service again will not cause another lookup as the client
+ already has a cached reference. It will therefore continue to use the bean instance
+ representing the initial service configuration.</para>
<note>
- <para>You can test this behaviour for yourself by typing 'u' followed by return to undeploy the current service. You should still be able to access the service from the client even though it is 'undeployed'. Now make some changes to the configuration using the jboss-beans.xml file and deploy it again using the 'd' option (remember to save any changes you make to jboss-beans.xml first). Printing out the status of the service using the 'p' option should allow you to see that the client is still accessing the initial instance of the service that was deployed.</para>
+ <para>You can test this behaviour for yourself by typing 'u' followed by return
+ to undeploy the current service. You should still be able to access the service from the
+ client even though it is 'undeployed'. Now make some changes to the
+ configuration using the jboss-beans.xml file and deploy it again using the 'd'
+ option (remember to save any changes you make to jboss-beans.xml first). Printing out
+ the status of the service using the 'p' option should allow you to see that
+ the client is still accessing the initial instance of the service that was
+ deployed.</para>
</note>
<warning>
- <para>Even if we change our client so that we lookup a new reference each time the service is redeployed there is nothing to prevent new developers from coming along and mistakedly handing out copies of this reference to other objects. If all of these references are not cleaned up during redeployment then this then puts us in the same situation as before with access to older instances of the service still possible.</para>
+ <para>Even if we change our client so that we lookup a new reference each time the service
+ is redeployed there is nothing to prevent new developers from coming along and
+ mistakedly handing out copies of this reference to other objects. If all of these
+ references are not cleaned up during redeployment then this then puts us in the same
+ situation as before with access to older instances of the service still possible.</para>
</warning>
- <para>To reliably redeploy the reconfigured service you must therefore shutdown the application completely using the 'q' option and restart it again using the <code>run.sh</code> script. For enterprise services such as transactions, messaging and persistance this is perfectly acceptable since they are generally always in use. As such they cannot be redeployed at runtime and benefit from the high performance given by using direct access. If your service falls into this category then you too should consider using direct access via the microcontainer controller.</para>
+ <para>To reliably redeploy the reconfigured service you must therefore shutdown the
+ application completely using the 'q' option and restart it again using the
+ <code>run.sh</code> script. For enterprise services such as transactions, messaging and
+ persistance this is perfectly acceptable since they are generally always in use. As such
+ they cannot be redeployed at runtime and benefit from the high performance given by using
+ direct access. If your service falls into this category then you too should consider using
+ direct access via the microcontainer controller.</para>
</section>
<section>
<title>Indirect access</title>
- <para>The <code>run.sh</code> script can be called with an optional parameter 'bus' to specify that calls to the Human Resources service should take place using the microcontainer bus:</para>
+ <para>The <code>run.sh</code> script can be called with an optional parameter
+ 'bus' to specify that calls to the Human Resources service should take place
+ using the microcontainer bus:</para>
<programlisting>./run.sh bus</programlisting>
- <para>Instead of using a direct reference to the bean instance obtained from the microcontainer controller we now call an invoke() method on the bus passing in the bean name, method name, method arguments and method types. The bus takes this information and uses it to call the bean on the client's behalf.</para>
+ <para>Instead of using a direct reference to the bean instance obtained from the
+ microcontainer controller we now call an invoke() method on the bus passing in the bean
+ name, method name, method arguments and method types. The bus takes this information and
+ uses it to call the bean on the client's behalf.</para>
<programlisting role="JAVA">private final static String HRSERVICE = "HRService";
...
@@ -539,24 +894,70 @@
}
return result;
}</programlisting>
- <para>Behind the scenes the bus looks up the reference to the bean instance specified by the bean name and calls the chosen method using reflecton. Since the client never has a direct reference to the bean instance we say that it accesses the service 'indirectly'. As the bus does not cache the reference we can safely make changes to our service configuration and redeploy it at runtime. Subsequent calls by the client will then result in the new reference to the service being used as expected. In technical terms we say that the client and service have been decoupled.</para>
+ <para>Behind the scenes the bus looks up the reference to the bean instance specified by the
+ bean name and calls the chosen method using reflecton. Since the client never has a direct
+ reference to the bean instance we say that it accesses the service 'indirectly'.
+ As the bus does not cache the reference we can safely make changes to our service
+ configuration and redeploy it at runtime. Subsequent calls by the client will then result
+ in the new reference to the service being used as expected. In technical terms we say that
+ the client and service have been decoupled.</para>
<note>
- <para>You can test this behaviour for yourself by deploying the service and using the 'p' option to print out the status. Undeploy the service using the 'u' option and check that you cannot access it anymore. Now make some changes to the configuration using the jboss-beans.xml file and deploy it again using the 'd' option (remember to save any changes you make to jboss-beans.xml first). Printing out the status again using the 'p' option should reveal that the client is accessing the new service configuration.</para>
+ <para>You can test this behaviour for yourself by deploying the service and using the
+ 'p' option to print out the status. Undeploy the service using the
+ 'u' option and check that you cannot access it anymore. Now make some changes
+ to the configuration using the jboss-beans.xml file and deploy it again using the
+ 'd' option (remember to save any changes you make to jboss-beans.xml first).
+ Printing out the status again using the 'p' option should reveal that the
+ client is accessing the new service configuration.</para>
</note>
- <para>As the bus uses reflection to call bean instances it is slower than using direct access. The benefit however is that only the bus has references to the bean instances. This means that when a service is redeployed we can clean up all of the existing references and replace them with new ones, allowing us to reliably redeploy a service at runtime. Services that are not used very often or that are specific to certain applications are good candidates for indirect access using the microcontainer bus. This is either because the reduction in performance does not matter if the service is used rarely or the service can be deployed and undeployed together with the relevant applications to keep the runtime environment clean.</para>
+ <para>As the bus uses reflection to call bean instances it is slower than using direct
+ access. The benefit however is that only the bus has references to the bean instances.
+ This means that when a service is redeployed we can clean up all of the existing
+ references and replace them with new ones, allowing us to reliably redeploy a service at
+ runtime. Services that are not used very often or that are specific to certain
+ applications are good candidates for indirect access using the microcontainer bus. This is
+ either because the reduction in performance does not matter if the service is used rarely
+ or the service can be deployed and undeployed together with the relevant applications to
+ keep the runtime environment clean.</para>
</section>
<section>
<title>Dynamic classloading</title>
- <para>So far we have been using the extension and application classloaders to load all of the classes in our application. The application classpath was setup by the <code>run.sh</code> script using the -cp flag to include the current directory and the client-1.0.0.jar.</para>
+ <para>So far we have been using the extension and application classloaders to load all of
+ the classes in our application. The application classpath was setup by the
+ <code>run.sh</code> script using the -cp flag to include the current directory and the
+ client-1.0.0.jar.</para>
<programlisting>java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1</programlisting>
- <para>For convenience the JARs in the lib directory were added to the extenion classloader's classpath using the <code>java.ext.dirs</code> system property as this prevents us from having to list the full path to each of the JARs after the -cp flag. Since the extension classloader is the parent of the application classloader our client classes can find all of the microcontainer classes together with the Human Resources service classes at runtime.</para>
+ <para>For convenience the JARs in the lib directory were added to the extenion
+ classloader's classpath using the <code>java.ext.dirs</code> system property as this
+ prevents us from having to list the full path to each of the JARs after the -cp flag.
+ Since the extension classloader is the parent of the application classloader our client
+ classes can find all of the microcontainer classes together with the Human Resources
+ service classes at runtime.</para>
<note>
- <para>If you are using Java 6+ then you can use a wildcard to include all JARs in a directory with the -cp flag.<programlisting>java -cp `pwd`/lib/*:.:client-1.0.0.jar org.jboss.example.client.Client $1</programlisting>This means that all of the classes in our application will be added to the application classloader's classpath and the extension classloader's classpath will retain its default value.</para>
+ <para>If you are using Java 6+ then you can use a wildcard to include all JARs in a
+ directory with the -cp
+ flag.<programlisting>java -cp `pwd`/lib/*:.:client-1.0.0.jar org.jboss.example.client.Client $1</programlisting>This
+ means that all of the classes in our application will be added to the application
+ classloader's classpath and the extension classloader's classpath will retain
+ its default value.</para>
</note>
- <para>This is all well and good but what happens if we now want to deploy an additional service at runtime? If the new service is packaged in a JAR file then it needs to be visible to a classloader before any of its classes can be loaded. The trouble is we have already setup the classpath for the application classloader (and extension classloader) on startup so we cannot easily add the url of the JAR. The same situation applies if the service classes are contained in a directory structure. Unless the top-level directory is located in the current directory (which is on the application classpath) then the classes will not be found by the application classloader.</para>
- <para>It's also possible that we may wish to redeploy an existing service with changes to some of its classes. Since it is forbidden for an existing classloader to reload classes due to security constraints how can this be done?</para>
- <para>What we need is the ability to create a new classloader that knows the location of the new service's classes, or that can load new versions of an existing service's classes, so that we can deploy the service's beans. JBoss Microcontainer allows us to do this using the <classloader> element in the deployment descriptor.</para>
- <para>The client-cl distribution in the <code>commandLineClient/target/client-cl.dir</code> directory contains the following files that demonstrate how this works: </para>
+ <para>This is all well and good but what happens if we now want to deploy an additional
+ service at runtime? If the new service is packaged in a JAR file then it needs to be
+ visible to a classloader before any of its classes can be loaded. The trouble is we have
+ already setup the classpath for the application classloader (and extension classloader) on
+ startup so we cannot easily add the url of the JAR. The same situation applies if the
+ service classes are contained in a directory structure. Unless the top-level directory is
+ located in the current directory (which is on the application classpath) then the classes
+ will not be found by the application classloader.</para>
+ <para>It's also possible that we may wish to redeploy an existing service with changes
+ to some of its classes. Since it is forbidden for an existing classloader to reload
+ classes due to security constraints how can this be done?</para>
+ <para>What we need is the ability to create a new classloader that knows the location of the
+ new service's classes, or that can load new versions of an existing service's
+ classes, so that we can deploy the service's beans. JBoss Microcontainer allows us to
+ do this using the <classloader> element in the deployment descriptor.</para>
+ <para>The client-cl distribution in the <code>commandLineClient/target/client-cl.dir</code>
+ directory contains the following files that demonstrate how this works: </para>
<programlisting>run.sh
client-1.0.0.jar
jboss-beans.xml
@@ -572,9 +973,14 @@
/log4j-1.2.14.jar
/xercesImpl-2.7.1.jar
otherLib/humanResourcesService-1.0.0.jar</programlisting>
- <para>As you can see the humanResourcesService.jar file has been moved to a new subdirectory called <code>otherLib</code>. In this location it is no longer available to either the extension or application classloaders whose classpaths are setup in the <code>run.sh</code> script:</para>
+ <para>As you can see the humanResourcesService.jar file has been moved to a new subdirectory
+ called <code>otherLib</code>. In this location it is no longer available to either the
+ extension or application classloaders whose classpaths are setup in the
+ <code>run.sh</code> script:</para>
<programlisting>java -Djava.ext.dirs=`pwd`/lib -cp .:client-1.0.0.jar org.jboss.example.client.Client $1</programlisting>
- <para>We must therefore create a new classloader during the deployment of the service so that we can load in the service classes and create instances of the beans. You can see how this is done by looking at the contents of the jboss-beans.xml file:</para>
+ <para>We must therefore create a new classloader during the deployment of the service so
+ that we can load in the service classes and create instances of the beans. You can see how
+ this is done by looking at the contents of the jboss-beans.xml file:</para>
<programlisting role="XML"><?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -614,9 +1020,20 @@
</bean> -->
</deployment></programlisting>
- <para>First of all we create an instance of java.net.URL called URL using parameter injection in the constructor to specify the location of the humanResourcesService.jar file on our local filesystem. Then we create an instance of a URLClassLoader by injecting the URL bean into the constructor as the only element in an array. Once this is done then we simply include a <classloader> element in our HRService bean definition and inject the customCL bean. This specifies that the HRManager class needs to be loaded by the customCL classloader.</para>
- <para>But how do we know which classloader to use for the other beans in the deployment?</para>
- <para>The answer is that all beans in the deployment use the current thread's context classloader. In our case the thread that handles deployment is the main thread of the application which has its context classloader set to the application classloader on startup. If you wish, you can choose to specify a different classloader for the entire deployment using a <classloader> element as follows:</para>
+ <para>First of all we create an instance of java.net.URL called URL using parameter
+ injection in the constructor to specify the location of the humanResourcesService.jar file
+ on our local filesystem. Then we create an instance of a URLClassLoader by injecting the
+ URL bean into the constructor as the only element in an array. Once this is done then we
+ simply include a <classloader> element in our HRService bean definition and inject
+ the customCL bean. This specifies that the HRManager class needs to be loaded by the
+ customCL classloader.</para>
+ <para>But how do we know which classloader to use for the other beans in the
+ deployment?</para>
+ <para>The answer is that all beans in the deployment use the current thread's context
+ classloader. In our case the thread that handles deployment is the main thread of the
+ application which has its context classloader set to the application classloader on
+ startup. If you wish, you can choose to specify a different classloader for the entire
+ deployment using a <classloader> element as follows:</para>
<programlisting role="XML"><?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -644,26 +1061,73 @@
...
</deployment></programlisting>
- <para>This would be necessary for example if you wished to reconfigure the service by uncommenting the AgeBasedSalary or LocationBasedSalary beans. As you might expect, classloaders specified at the bean level override the deployment level classloader and if you wish to ignore the deployment level classloader altogether, and use the default classloader for a bean, then you can use the <null/> value as follows:</para>
+ <para>This would be necessary for example if you wished to reconfigure the service by
+ uncommenting the AgeBasedSalary or LocationBasedSalary beans. As you might expect,
+ classloaders specified at the bean level override the deployment level classloader and if
+ you wish to ignore the deployment level classloader altogether, and use the default
+ classloader for a bean, then you can use the <null/> value as follows:</para>
<programlisting role="XML"> <bean name="HRService" class="org.jboss.example.service.HRManager">
<classloader><null/></classloader>
</bean></programlisting>
<warning>
- <para>If you decide to create a new classloader for your service using the deployment descriptor then be aware that you may not be able to access classes loaded by it from the application classloader anymore. In our example this means that the client will no longer be able to cache a direct reference to the bean instance when using the microcontainer controller. You can see this for yourself by starting the client using the <code>run.sh</code> command and then trying to deploy the service. You should see that a java.lang.NoClassDefFoundError exception is thrown and the application will then exit.</para>
- <para>You must therefore use the bus to access the service indirectly and provide access to any classes shared by the client in the application classpath. In our example this means the Address, Employee, and SalaryStrategy classes.</para>
+ <para>If you decide to create a new classloader for your service using the deployment
+ descriptor then be aware that you may not be able to access classes loaded by it from
+ the application classloader anymore. In our example this means that the client will no
+ longer be able to cache a direct reference to the bean instance when using the
+ microcontainer controller. You can see this for yourself by starting the client using
+ the <code>run.sh</code> command and then trying to deploy the service. You should see
+ that a java.lang.NoClassDefFoundError exception is thrown and the application will then
+ exit.</para>
+ <para>You must therefore use the bus to access the service indirectly and provide access
+ to any classes shared by the client in the application classpath. In our example this
+ means the Address, Employee, and SalaryStrategy classes.</para>
</warning>
</section>
</chapter>
<chapter>
<title>Adding behaviour with AOP</title>
- <para>Object Oriented Programming (OOP) contains many useful techniques for software development including encapsulation, inheritance, and polymorphism but does not solve the problem of how to address logic that is often repeated in many different classes. Examples of this include logging, security, and transactional logic which is traditionally hard-coded into each class making the source code difficult to maintain. We call logic of this nature a 'cross-cutting concern' as it typically applies across class hierarchies.</para>
- <para>Aspect Oriented Programming (AOP) provides a solution to this by allowing cross-cutting concerns to be applied to classes after they have been compiled. This keeps the source code free of logic which is not central to the main purpose of the class and aids maintenance. The way it is done varies depending on the AOP implementation. Typically if a class implements an interface then all method calls to an instance of the class first pass through a proxy that implements the same interface and adds in the required behaviour. Alternatively, if an interface is not used, then the java bytecode of the compiled class is modified so that the original methods are renamed and replaced by methods that implement the cross-cutting logic. These new methods then call the original methods after the cross-cutting logic has been executed. The same result can be achieved by modifying the bytecode to create a subclass of the original class that overrides its methods. The over!
riden methods then execute the cross-cutting logic before calling the corresponding methods of the super class. </para>
- <para>JBoss AOP is a framework for Aspect-Oriented Programming that allows you to create cross-cutting concerns using conventional java classes and methods. In AOP terminology each concern is represented by an aspect that you implement using a simple POJO. Behaviour is provided by methods within the aspect called advices that follow certain rules for their parameter and return types together with any exceptions that they throw. Other than this you are free to use conventional object-oriented notions such as inheritance, encapsulation, and composition in order to make your cross-cutting concerns as maintainable as possible. Aspects are applied to code using an expression language that allows you to specify which constructors, methods and even fields to target. This means that you can quickly change the behaviour of a number of classes simply by editing a configuration file. </para>
- <para>In this chapter we are going to look at using JBoss AOP together with the microcontainer to create and apply an auditing aspect to the Human Resources Service. We could choose to place auditing code within the HRManager class but it would detract from the main purpose of the class and add unnecessary complexity. The design of the aspect will ensure that it can also be used with other classes if our requirements change at a later date.</para>
- <para>We shall also look at how AOP can be used to apply additional behaviour during the deployment phase. Specifically we will show how you can create and bind a proxy to a bean instance into a basic JNDI service so that you can access it using a JNDI lookup instead of the microcontainer controller.</para>
+ <para>Object Oriented Programming (OOP) contains many useful techniques for software
+ development including encapsulation, inheritance, and polymorphism but does not solve the
+ problem of how to address logic that is often repeated in many different classes. Examples
+ of this include logging, security, and transactional logic which is traditionally hard-coded
+ into each class making the source code difficult to maintain. We call logic of this nature a
+ 'cross-cutting concern' as it typically applies across class hierarchies.</para>
+ <para>Aspect Oriented Programming (AOP) provides a solution to this by allowing cross-cutting
+ concerns to be applied to classes after they have been compiled. This keeps the source code
+ free of logic which is not central to the main purpose of the class and aids maintenance.
+ The way it is done varies depending on the AOP implementation. Typically if a class
+ implements an interface then all method calls to an instance of the class first pass through
+ a proxy that implements the same interface and adds in the required behaviour.
+ Alternatively, if an interface is not used, then the java bytecode of the compiled class is
+ modified so that the original methods are renamed and replaced by methods that implement the
+ cross-cutting logic. These new methods then call the original methods after the
+ cross-cutting logic has been executed. The same result can be achieved by modifying the
+ bytecode to create a subclass of the original class that overrides its methods. The
+ overriden methods then execute the cross-cutting logic before calling the corresponding
+ methods of the super class. </para>
+ <para>JBoss AOP is a framework for Aspect-Oriented Programming that allows you to create
+ cross-cutting concerns using conventional java classes and methods. In AOP terminology each
+ concern is represented by an aspect that you implement using a simple POJO. Behaviour is
+ provided by methods within the aspect called advices that follow certain rules for their
+ parameter and return types together with any exceptions that they throw. Other than this you
+ are free to use conventional object-oriented notions such as inheritance, encapsulation, and
+ composition in order to make your cross-cutting concerns as maintainable as possible.
+ Aspects are applied to code using an expression language that allows you to specify which
+ constructors, methods and even fields to target. This means that you can quickly change the
+ behaviour of a number of classes simply by editing a configuration file. </para>
+ <para>In this chapter we are going to look at using JBoss AOP together with the microcontainer
+ to create and apply an auditing aspect to the Human Resources Service. We could choose to
+ place auditing code within the HRManager class but it would detract from the main purpose of
+ the class and add unnecessary complexity. The design of the aspect will ensure that it can
+ also be used with other classes if our requirements change at a later date.</para>
+ <para>We shall also look at how AOP can be used to apply additional behaviour during the
+ deployment phase. Specifically we will show how you can create and bind a proxy to a bean
+ instance into a basic JNDI service so that you can access it using a JNDI lookup instead of
+ the microcontainer controller.</para>
<section>
<title>Creating an aspect</title>
- <para>The <code>examples/User_Guide/gettingStarted/auditAspect</code> directory contains all the files you need to create the aspect.</para>
+ <para>The <code>examples/User_Guide/gettingStarted/auditAspect</code> directory contains all
+ the files you need to create the aspect.</para>
<programlisting>auditAspect/pom.xml
/src/main/java/org/jboss/example/aspect/AuditAspect.java</programlisting>
<para>As you can see from the source code below all we need is a simple POJO.</para>
@@ -719,15 +1183,35 @@
return retVal;
}
}</programlisting>
- <para>The constructor checks for the presence of a <code>log</code> directory in the current working directory and creates one if not found. After this we define an advice that will be called whenever the constructor of our target class is called. The purpose of this is to create a new log file within the log directory so that we can record method calls made on different instances of our target class in separate files. Finally we define an advice that will apply to each method call made on the target class. Here we store the method name and arguments together with the return value so that we can construct an audit record and write it to the current log file. Notice how each advice calls <code>inv.invokeNext()</code>. This is required in order to call the next advice, if more than one cross-cutting concern has been applied, or to call the target constructor/method.</para>
+ <para>The constructor checks for the presence of a <code>log</code> directory in the current
+ working directory and creates one if not found. After this we define an advice that will
+ be called whenever the constructor of our target class is called. The purpose of this is
+ to create a new log file within the log directory so that we can record method calls made
+ on different instances of our target class in separate files. Finally we define an advice
+ that will apply to each method call made on the target class. Here we store the method
+ name and arguments together with the return value so that we can construct an audit record
+ and write it to the current log file. Notice how each advice calls
+ <code>inv.invokeNext()</code>. This is required in order to call the next advice, if
+ more than one cross-cutting concern has been applied, or to call the target
+ constructor/method.</para>
<note>
- <para>Each advice is implemented using a method that takes an invocation object as a parameter, throws Throwable and returns Object. This is necessary as we do not know at design time which constructors or methods our advices will be applied to so the types must be as generic as possible. For more information on creating aspects and advices together with additional examples of how to apply them to various classes please consult the JBoss AOP project documentation.</para>
+ <para>Each advice is implemented using a method that takes an invocation object as a
+ parameter, throws Throwable and returns Object. This is necessary as we do not know at
+ design time which constructors or methods our advices will be applied to so the types
+ must be as generic as possible. For more information on creating aspects and advices
+ together with additional examples of how to apply them to various classes please consult
+ the JBoss AOP project documentation.</para>
</note>
- <para>To compile the class and create an auditAspect.jar file that can be used by other examples simply type <code>mvn install</code> from the auditAspect directory.</para>
+ <para>To compile the class and create an auditAspect.jar file that can be used by other
+ examples simply type <code>mvn install</code> from the auditAspect directory.</para>
</section>
<section>
<title>Configuring the microcontainer</title>
- <para>Before we can apply the audit aspect to our service we must first add a number of JARs to the extension classpath. These can be found in the <code>lib</code> subdirectory of the client-aop distribution located in the <code>examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir</code> directory:</para>
+ <para>Before we can apply the audit aspect to our service we must first add a number of JARs
+ to the extension classpath. These can be found in the <code>lib</code> subdirectory of the
+ client-aop distribution located in the
+ <code>examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir</code>
+ directory:</para>
<programlisting>run.sh
client-1.0.0.jar
jboss-beans.xml
@@ -748,14 +1232,28 @@
/log4j-1.2.14.jar
/<code>trove-2.1.1.jar</code>
/xercesImpl-2.7.1.jar</programlisting>
- <para>First of all we need to include the <code>auditAspect</code> jar as we need to create an instance of our aspect at runtime in order to execute the logic. We then need to include the jar file for JBoss AOP (<code>jboss-aop</code>) together with its dependencies; <code>javassist</code> and <code>trove</code>. Finally we need to add the<code> jboss-aop-mc-int</code> jar as this contains an XML schema definition that allows us to define aspects inside our XML deployment descriptor. It also contains integration code to create dependencies between normal beans and aspect beans within the microcontainer so that we can add behaviour during the deployment and undeployment phases.</para>
+ <para>First of all we need to include the <code>auditAspect</code> jar as we need to create
+ an instance of our aspect at runtime in order to execute the logic. We then need to
+ include the jar file for JBoss AOP (<code>jboss-aop</code>) together with its
+ dependencies; <code>javassist</code> and <code>trove</code>. Finally we need to add
+ the<code> jboss-aop-mc-int</code> jar as this contains an XML schema definition that
+ allows us to define aspects inside our XML deployment descriptor. It also contains
+ integration code to create dependencies between normal beans and aspect beans within the
+ microcontainer so that we can add behaviour during the deployment and undeployment
+ phases.</para>
<note>
- <para>Since we are using Maven2 to assemble the client-aop distribution we can easily add these JAR files by declaring the appropriate dependencies in our <code>pom.xml</code> file and creating a valid assembly descriptor. If you are using Ant to perform your build then you will need to do this in a different way.</para>
+ <para>Since we are using Maven2 to assemble the client-aop distribution we can easily add
+ these JAR files by declaring the appropriate dependencies in our <code>pom.xml</code>
+ file and creating a valid assembly descriptor. If you are using Ant to perform your
+ build then you will need to do this in a different way.</para>
</note>
</section>
<section>
<title>Applying an aspect</title>
- <para>Now that we have a valid distribution containing everything we need we can configure our jboss-beans.xml file to apply the audit aspect. This can be found in the <code>examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir</code> directory:</para>
+ <para>Now that we have a valid distribution containing everything we need we can configure
+ our jboss-beans.xml file to apply the audit aspect. This can be found in the
+ <code>examples/User_Guide/gettingStarted/commandLineClient/target/client-aop.dir</code>
+ directory:</para>
<programlisting role="XML"><?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -779,10 +1277,33 @@
...
</deployment></programlisting>
- <para>Before we can apply our aspect to any classes we need to create an instance of org.jboss.aop.AspectManager using a <bean> element. We use a factory method instead of calling a conventional constructor as we only want one instance of the AspectManager in the JVM at runtime i.e. it is a singleton.</para>
- <para>Next we declare an aspect called AuditAspect using the <aop:aspect> element. This looks much like the <bean> element as it has <code>name</code> and <code>class</code> attributes that you can use in the same way. However, behind the scenes due to the <code>urn:jboss:aop-beans:1.0</code> namespace this resolves to some other beans which install the aspect into the JBoss Microcontainer and into the JBoss AOP AspectManager.</para>
- <para>Next we declare a binding called AuditAspect using the <aop:bind> element. Again, this resolves to some other beans which install the binding into the JBoss Microcontainer and into the JBoss AOP AspectManager. The <code>pointcut</code> attribute says this binding should apply to all public constructors and all public methods of the <code>HRManager</code> class, and the nested <aop:advice> element specifies that when any of these are called we should invoke the <code>audit</code> advice method of the aspect called <code>AuditAspect</code>. We only need to specify one method called audit as we have overloaded this method within our AuditAspect class with different parameters. JBoss AOP knows at runtime which one to select based on whether a constructor or method invocation is being made.</para>
- <para>This additional configuration is all we need to apply the audit aspect at runtime and add auditing behaviour to the Human Resources service. You can test this out for yourself by running the client using the <code>run.sh</code> script. A <code>log</code> directory will be created on startup alongside the <code>lib</code> directory as the AuditAspect bean is created by the microcontainer. Each deployment of the Human Resources service will then cause a new log file to appear within the <code>log</code> directory containing a record of any calls made from the client to the service:</para>
+ <para>Before we can apply our aspect to any classes we need to create an instance of
+ org.jboss.aop.AspectManager using a <bean> element. We use a factory method instead
+ of calling a conventional constructor as we only want one instance of the AspectManager in
+ the JVM at runtime i.e. it is a singleton.</para>
+ <para>Next we declare an aspect called AuditAspect using the <aop:aspect> element.
+ This looks much like the <bean> element as it has <code>name</code> and
+ <code>class</code> attributes that you can use in the same way. However, behind the
+ scenes due to the <code>urn:jboss:aop-beans:1.0</code> namespace this resolves to some
+ other beans which install the aspect into the JBoss Microcontainer and into the JBoss AOP
+ AspectManager.</para>
+ <para>Next we declare a binding called AuditAspect using the <aop:bind> element.
+ Again, this resolves to some other beans which install the binding into the JBoss
+ Microcontainer and into the JBoss AOP AspectManager. The <code>pointcut</code> attribute
+ says this binding should apply to all public constructors and all public methods of the
+ <code>HRManager</code> class, and the nested <aop:advice> element specifies that
+ when any of these are called we should invoke the <code>audit</code> advice method of the
+ aspect called <code>AuditAspect</code>. We only need to specify one method called audit as
+ we have overloaded this method within our AuditAspect class with different parameters.
+ JBoss AOP knows at runtime which one to select based on whether a constructor or method
+ invocation is being made.</para>
+ <para>This additional configuration is all we need to apply the audit aspect at runtime and
+ add auditing behaviour to the Human Resources service. You can test this out for yourself
+ by running the client using the <code>run.sh</code> script. A <code>log</code> directory
+ will be created on startup alongside the <code>lib</code> directory as the AuditAspect
+ bean is created by the microcontainer. Each deployment of the Human Resources service will
+ then cause a new log file to appear within the <code>log</code> directory containing a
+ record of any calls made from the client to the service:</para>
<programlisting>log/auditLog-28112007-163902
/auditLog-28112007-164055
/auditLog-28112007-164108</programlisting>
@@ -794,54 +1315,82 @@
Method: isHiringFreeze Return: false
Method: getEmployees Return: [(Santa Claus, 1 Reindeer Avenue, Lapland City - 25/12/1860)]
Method: getSalaryStrategy</programlisting>
- <para>If you wish to remove the auditing behaviour then you can simply comment out the relevant fragments of XML in the deployment descriptor and restart the application.</para>
+ <para>If you wish to remove the auditing behaviour then you can simply comment out the
+ relevant fragments of XML in the deployment descriptor and restart the application.</para>
<warning>
- <para>The order of deployment for aspects declared in this way relative to normal beans matters in the deployment descriptor. Specifically each aspect must be declared before the beans that it applies to so that the microcontainer deploys them in that order. This is because the microcontainer may need to alter the bytecode of the normal bean class in order to add the cross-cutting logic before it creates an instance and stores a reference to it in the controller. If a normal bean instance has already been created then this is not possible. </para>
+ <para>The order of deployment for aspects declared in this way relative to normal beans
+ matters in the deployment descriptor. Specifically each aspect must be declared before
+ the beans that it applies to so that the microcontainer deploys them in that order. This
+ is because the microcontainer may need to alter the bytecode of the normal bean class in
+ order to add the cross-cutting logic before it creates an instance and stores a
+ reference to it in the controller. If a normal bean instance has already been created
+ then this is not possible. </para>
</warning>
</section>
<section>
<title>Lifecycle callbacks</title>
- <para>In addition to applying aspects to beans that we instantiate using the microcontainer we can also add behaviour during the deployment and undeployment process. As you may recall from the Direct access section of Chapter 4, a bean goes through several different states as it is deployed. These include:<itemizedlist>
+ <para>In addition to applying aspects to beans that we instantiate using the microcontainer
+ we can also add behaviour during the deployment and undeployment process. As you may
+ recall from the Direct access section of Chapter 4, a bean goes through several different
+ states as it is deployed. These include:<itemizedlist>
<listitem>
- <para>NOT_INSTALLED - the deployment descriptor containing the bean has been parsed along with any annotations on the bean itself.</para>
+ <para>NOT_INSTALLED - the deployment descriptor containing the bean has been parsed
+ along with any annotations on the bean itself.</para>
</listitem>
<listitem>
- <para>DESCRIBED - any dependencies created by AOP have been added to the bean and custom annotations have been processed.</para>
+ <para>DESCRIBED - any dependencies created by AOP have been added to the bean and
+ custom annotations have been processed.</para>
</listitem>
<listitem>
<para>INSTANTIATED - an instance of the bean has been created.</para>
</listitem>
<listitem>
- <para>CONFIGURED - properties have been injected into the bean along with any references to other beans.</para>
+ <para>CONFIGURED - properties have been injected into the bean along with any
+ references to other beans.</para>
</listitem>
<listitem>
- <para>CREATE - the create method, if defined on the bean, has been called.</para>
+ <para>CREATE - the create method, if defined on the bean, has been called.</para>
</listitem>
<listitem>
<para>START - the start method, if defined on the bean, has been called.</para>
</listitem>
<listitem>
- <para>INSTALLED - any custom install actions that were defined in the deployment descriptor have been executed and the bean is ready to access.</para>
+ <para>INSTALLED - any custom install actions that were defined in the deployment
+ descriptor have been executed and the bean is ready to access.</para>
</listitem>
</itemizedlist></para>
<important>
- <para>The CREATE and START states are included in order to allow services that used to be implemented as MBeans in JBoss AS 3.x and 4.x to function correctly when implemented as beans in JBoss AS 5.x. If you do not define any corresponding create/start methods in your bean then it will simply pass straight through these states.</para>
+ <para>The CREATE and START states are included in order to allow services that used to be
+ implemented as MBeans in JBoss AS 3.x and 4.x to function correctly when implemented as
+ beans in JBoss AS 5.x. If you do not define any corresponding create/start methods in
+ your bean then it will simply pass straight through these states.</para>
</important>
- <para>Together these states represent the bean's lifecycle and using an additional set of <aop> elements you can define a number of callbacks to be applied to any point:</para>
+ <para>Together these states represent the bean's lifecycle and using an additional set
+ of <aop> elements you can define a number of callbacks to be applied to any
+ point:</para>
<programlisting><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</programlisting>
- <para>Just like the <bean> element and the <aop:aspect> element the <aop:lifecycle-> elements contain <code>name</code> and <code>class</code> attributes. These allow the microcontainer to create an instance of the callback class and give it a name so that it can be used as beans enter/leave the relevant state during deployment and undeployment. You can specify which beans are affected by the callback using the <code>classes</code> attribute:</para>
+ <para>Just like the <bean> element and the <aop:aspect> element the
+ <aop:lifecycle-> elements contain <code>name</code> and <code>class</code>
+ attributes. These allow the microcontainer to create an instance of the callback class and
+ give it a name so that it can be used as beans enter/leave the relevant state during
+ deployment and undeployment. You can specify which beans are affected by the callback
+ using the <code>classes</code> attribute:</para>
<programlisting role="XML"><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></programlisting>
- <para>Here we have specified that additional logic in the LifecycleCallback class should be 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. </para>
- <para>In order for the callback class to work it must contain <code>install</code> and <code>uninstall</code> methods that take ControllerContext as a parameter:</para>
+ <para>Here we have specified that additional logic in the LifecycleCallback class should be
+ 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. </para>
+ <para>In order for the callback class to work it must contain <code>install</code> and
+ <code>uninstall</code> methods that take ControllerContext as a parameter:</para>
<programlisting role="JAVA">import org.jboss.dependency.spi.ControllerContext;
public class LifecycleCallback {
@@ -854,14 +1403,25 @@
System.out.println("Bean " + ctx.getName() + " is being uninstalled";
}
} </programlisting>
- <para>The install method will be called during the bean's deployment and the uninstall method during its undeployment.</para>
+ <para>The install method will be called during the bean's deployment and the uninstall
+ method during its undeployment.</para>
<note>
- <para>Although we are adding behaviour to the deployment and undeployment process using callbacks we are not actually using AOP to achieve this. The reason we have included them in this section, and the reason why they are part of the aop XML schema, is that they use the pointcut expression functionality of JBoss AOP to determine which bean classes they should apply to. We have already shown how the <code>classes</code> attribute allows you to write a shorthand pointcut expression to target annotated bean classes. Later in Part III - AOP Development we will show how it is possible to use regular pointcut expressions to target classes in a much more powerful way.</para>
+ <para>Although we are adding behaviour to the deployment and undeployment process using
+ callbacks we are not actually using AOP to achieve this. The reason we have included
+ them in this section, and the reason why they are part of the aop XML schema, is that
+ they use the pointcut expression functionality of JBoss AOP to determine which bean
+ classes they should apply to. We have already shown how the <code>classes</code>
+ attribute allows you to write a shorthand pointcut expression to target annotated bean
+ classes. Later in Part III - AOP Development we will show how it is possible to use
+ regular pointcut expressions to target classes in a much more powerful way.</para>
</note>
</section>
<section>
<title>Adding service lookup through JNDI</title>
- <para>Until now we have looked up references to bean instances representing services through the microcontainer controller. Whilst there is nothing wrong with this it is not ideal since we must first have a reference to the microcontainer kernel before we can access the controller: </para>
+ <para>Until now we have looked up references to bean instances representing services through
+ the microcontainer controller. Whilst there is nothing wrong with this it is not ideal
+ since we must first have a reference to the microcontainer kernel before we can access the
+ controller: </para>
<programlisting role="JAVA">private HRManager manager;
private EmbeddedBootstrap bootstrap;
@@ -883,33 +1443,52 @@
ControllerContext context = controller.getInstalledContext(HRSERVICE);
if (context != null) { manager = (HRManager) context.getTarget(); }</programlisting>
- <para>Handing out kernel references to every client that looks up a service represents a significant risk as it provides wide-spread access to the microcontainer configuration. It would be better to apply the ServiceLocator pattern and have a class that performs lookups on behalf of the clients. Better still we could pass the bean references, together with their names, to the ServiceLocator at deployment time using a lifecycle callback so that it could look them up without knowing about the microcontainer at all. Undeployment would subsequently remove the bean references from the ServiceLocator to prevent further lookups. </para>
- <para>While it would not be too difficult to write our own ServiceLocator implementation we can save time by integrating an existing one such as JBoss Naming Service (JBoss NS). This has the additional benefit of complying to the Java Naming and Directory Interface (JNDI) specification. JNDI enables clients to access different, possibly multiple, naming services using a common API. </para>
- <para>All that we need to do is create an instance of JBoss NS using the microcontainer and then add a lifecycle callback to perform the binding and unbinding of our bean references during deployment/undeployment. We can then mark the bean classes that we wish to bind references for using annotations and find them at runtime using the shorthand pointcut expression as shown earlier.</para>
+ <para>Handing out kernel references to every client that looks up a service represents a
+ significant risk as it provides wide-spread access to the microcontainer configuration. It
+ would be better to apply the ServiceLocator pattern and have a class that performs lookups
+ on behalf of the clients. Better still we could pass the bean references, together with
+ their names, to the ServiceLocator at deployment time using a lifecycle callback so that
+ it could look them up without knowing about the microcontainer at all. Undeployment would
+ subsequently remove the bean references from the ServiceLocator to prevent further
+ lookups. </para>
+ <para>While it would not be too difficult to write our own ServiceLocator implementation we
+ can save time by integrating an existing one such as JBoss Naming Service (JBoss NS). This
+ has the additional benefit of complying to the Java Naming and Directory Interface (JNDI)
+ specification. JNDI enables clients to access different, possibly multiple, naming
+ services using a common API. </para>
+ <para>All that we need to do is create an instance of JBoss NS using the microcontainer and
+ then add a lifecycle callback to perform the binding and unbinding of our bean references
+ during deployment/undeployment. We can then mark the bean classes that we wish to bind
+ references for using annotations and find them at runtime using the shorthand pointcut
+ expression as shown earlier.</para>
</section>
</chapter>
<chapter>
<title>Advanced deployment</title>
- <para>Mention the need for a main deployer and explain how we add various deployers to this.</para>
+ <para>Mention the need for a main deployer and explain how we add various deployers to
+ this.</para>
<section>
<title>Deployment stages and actions</title>
- <para>Give example of using aspectized bean deployer and show how it's the same as the BasicXMLDeployer.</para>
+ <para>Give example of using aspectized bean deployer and show how it's the same as the
+ BasicXMLDeployer.</para>
</section>
<section>
<title>Changing the descriptor file format</title>
- <para>Give an example where we move the jboss-beans.xml file out of the META-INF directory and change the location of the classes.</para>
+ <para>Give an example where we move the jboss-beans.xml file out of the META-INF directory
+ and change the location of the classes.</para>
</section>
<section>
<title>Changing the archive structure</title>
- <para>Give an example of changing the jboss-beans.xml file for a jboss-beans.properties file.</para>
+ <para>Give an example of changing the jboss-beans.xml file for a jboss-beans.properties
+ file.</para>
</section>
<section>
<title>Changing the classloader</title>
- <para/>
+ <para />
</section>
<section>
<title>Adding a deployment action</title>
- <para/>
+ <para />
</section>
</chapter>
</part>
@@ -917,11 +1496,18 @@
<title>POJO Development</title>
<chapter>
<title>Introduction</title>
- <para>In Part I of this guide we looked at a complete use-case showing how to develop a service using POJOs and access it from one or more clients. This should have given you a good overview of how the microcontainer is intended to be used in real applications. In Part II we look more closely at the various features available for creating POJOs, wiring them together, deploying them into the runtime environment, and managing their dependencies. With this knowledge you should be able to create flexible and reusable services that can be integrated together to provide a custom runtime suited to your needs.</para>
+ <para>In Part I of this guide we looked at a complete use-case showing how to develop a
+ service using POJOs and access it from one or more clients. This should have given you a
+ good overview of how the microcontainer is intended to be used in real applications. In Part
+ II we look more closely at the various features available for creating POJOs, wiring them
+ together, deploying them into the runtime environment, and managing their dependencies. With
+ this knowledge you should be able to create flexible and reusable services that can be
+ integrated together to provide a custom runtime suited to your needs.</para>
</chapter>
<chapter>
<title>Running the examples</title>
- <para>You can find examples of how to use each of the features described here in the <code>examples/User_Guide/pojoDevelopment</code> directory:</para>
+ <para>You can find examples of how to use each of the features described here in the
+ <code>examples/User_Guide/pojoDevelopment</code> directory:</para>
<programlisting>alias
annotations
autowiring
@@ -939,12 +1525,15 @@
simple
spring
supply</programlisting>
- <para>To build these simply <command>cd</command> to this directory and enter <command>mvn package</command>. A <code>target</code> directory will then be created in each subdirectory as follows:</para>
+ <para>To build these simply <command>cd</command> to this directory and enter <command>mvn
+ package</command>. A <code>target</code> directory will then be created in each
+ subdirectory as follows:</para>
<programlisting>target/archive-tmp
/classes
/<exampleName>-1.0.0.jar
/<exampleName>-dist.dir</programlisting>
- <para>The contents of the <exampleName>-dist.dir will be similar for each example:</para>
+ <para>The contents of the <exampleName>-dist.dir will be similar for each
+ example:</para>
<programlisting><exampleName>-1.0.0.jar
log4j.properties
lib/concurrent-1.3.4.jar
@@ -958,7 +1547,10 @@
/jbossxb-2.0.0.CR4.jar
/log4j-1.2.14.jar
/xercesImpl-2.7.1.jar</programlisting>
- <para>If you previously used the examples from Part I you will notice that we no longer have a <code>run.sh</code> script. Instead we have made <exampleName>-1.0.0.jar executable. This means that it contains a META-INF/MANIFEST.MF file with Main-Class and Class-Path attributes:</para>
+ <para>If you previously used the examples from Part I you will notice that we no longer have a
+ <code>run.sh</code> script. Instead we have made <exampleName>-1.0.0.jar executable.
+ This means that it contains a META-INF/MANIFEST.MF file with Main-Class and Class-Path
+ attributes:</para>
<programlisting>Main-Class: org.jboss.kernel.plugins.bootstrap.standalone.StandaloneBootstrap
Class-Path: .
lib/jboss-common-core-2.0.4.GA.jar
@@ -988,10 +1580,22 @@
lib/jboss-dependency-2.0.0.B eta6.jar
lib/ant-junit-1.6.5.jar</programlisting>
<note>
- <para>Certain JAR files included in the Class-Path attribute are not present in the <code>lib</code> directory. This is because the maven-jar-plugin is creating the Class-Path value using its internal dependency resolution mechanism and including JARs that are not actually needed at runtime. This is the fault of the pom.xml descriptors in projects such as jbossxb, which the microcontainer depends on, that do not currently mark their dependencies as optional. For clarity we have prevented all of these JARs appearing in the lib directory of the distribution by explicitly listing only those that are required in the <code>examples/User_Guide/pojoDevelopment/<code>dist.xml</code></code> file which is passed to the maven-assembly-plugin. </para>
- <para>Once we have fixed the pom.xml files in the projects that the microcontainer depends on then this filtering will no longer be necessary. </para>
+ <para>Certain JAR files included in the Class-Path attribute are not present in the
+ <code>lib</code> directory. This is because the maven-jar-plugin is creating the
+ Class-Path value using its internal dependency resolution mechanism and including JARs
+ that are not actually needed at runtime. This is the fault of the pom.xml descriptors in
+ projects such as jbossxb, which the microcontainer depends on, that do not currently mark
+ their dependencies as optional. For clarity we have prevented all of these JARs appearing
+ in the lib directory of the distribution by explicitly listing only those that are
+ required in the <code>examples/User_Guide/pojoDevelopment/<code>dist.xml</code></code>
+ file which is passed to the maven-assembly-plugin. </para>
+ <para>Once we have fixed the pom.xml files in the projects that the microcontainer depends
+ on then this filtering will no longer be necessary. </para>
</note>
- <para>The Main-Class attribute contains the name of a StandaloneBootstrap class that is provided with the microcontainer. This class is very similar to the EmbeddedBootstrap class that we used in Part I with the addition of a main() method so that it can be called from the command line:</para>
+ <para>The Main-Class attribute contains the name of a StandaloneBootstrap class that is
+ provided with the microcontainer. This class is very similar to the EmbeddedBootstrap class
+ that we used in Part I with the addition of a main() method so that it can be called from
+ the command line:</para>
<programlisting role="JAVA">public class StandaloneBootstrap extends BasicBootstrap
{
protected BasicXMLDeployer deployer;
@@ -1048,9 +1652,18 @@
}
}
</programlisting>
- <para>The bootstrap() method also includes code to automatically scan the classpath for files called jboss-beans.xml (the value of StandaloneKernelConstants.DEPLOYMENT_XML_NAME) that may or may not reside in a META-INF directory. If any such files are found then their URLs are passed to the BasicXMLDeployer to deploy any beans declared inside. In our examples we place a jboss-beans.xml file into the META-INF directory of our <exampleName>-1.0.0.jar so that it can be found in this way.</para>
+ <para>The bootstrap() method also includes code to automatically scan the classpath for files
+ called jboss-beans.xml (the value of StandaloneKernelConstants.DEPLOYMENT_XML_NAME) that may
+ or may not reside in a META-INF directory. If any such files are found then their URLs are
+ passed to the BasicXMLDeployer to deploy any beans declared inside. In our examples we place
+ a jboss-beans.xml file into the META-INF directory of our <exampleName>-1.0.0.jar so
+ that it can be found in this way.</para>
<important>
- <para>To run any of the examples simply <command>cd</command> to the relevant <code>target/<exampleName>-dist.dir</code> directory and execute the JAR file using the <command>java -jar <exampleName>-1.0.0.jar</command> command, replacing <exampleName> with the name of the example you are running, .e.g <command>java -jar constructor-1.0.0.jar</command></para>
+ <para>To run any of the examples simply <command>cd</command> to the relevant
+ <code>target/<exampleName>-dist.dir</code> directory and execute the JAR file
+ using the <command>java -jar <exampleName>-1.0.0.jar</command> command, replacing
+ <exampleName> with the name of the example you are running, .e.g <command>java -jar
+ constructor-1.0.0.jar</command></para>
</important>
<para>Running an example causes the following sequence of events to occur:</para>
<orderedlist>
@@ -1058,16 +1671,21 @@
<para>The <exampleName>-1.0.0.jar is executed. </para>
</listitem>
<listitem>
- <para>The StandaloneBootstrap class defined in the Main-Class attribute of the META-INF/MANIFEST.MF file within the JAR is run which bootstraps the microcontainer and searches the classpath for files called jboss-beans.xml.</para>
+ <para>The StandaloneBootstrap class defined in the Main-Class attribute of the
+ META-INF/MANIFEST.MF file within the JAR is run which bootstraps the microcontainer and
+ searches the classpath for files called jboss-beans.xml.</para>
</listitem>
<listitem>
- <para>The META-INF/jboss-beans.xml file in the <exampleName>-1.0.0.jar is found and the beans declared within it are deployed.</para>
+ <para>The META-INF/jboss-beans.xml file in the <exampleName>-1.0.0.jar is found and
+ the beans declared within it are deployed.</para>
</listitem>
<listitem>
- <para>The main() method of the StandaloneBootstrap class ends and the JVM shuts down undeploying the beans in reverse order.</para>
+ <para>The main() method of the StandaloneBootstrap class ends and the JVM shuts down
+ undeploying the beans in reverse order.</para>
</listitem>
</orderedlist>
- <para>If you would like to use the StandaloneBootstrap class within your own applications to deploy beans on startup then you can do so as follows:</para>
+ <para>If you would like to use the StandaloneBootstrap class within your own applications to
+ deploy beans on startup then you can do so as follows:</para>
<programlisting role="JAVA">import org.jboss.kernel.plugins.bootstrap.standalone.StandaloneBootstrap;
public class MyApp {
@@ -1078,25 +1696,49 @@
}
}</programlisting>
<note>
- <para>Because the deploy() and undeploy() methods of the StandaloneBootstrap are protected you will not be able to call them from your application after the initial startup. If you wish to deploy and undeploy beans at runtime then the EmbeddedBootstrap class may be a better choice. Alternatively you are free to create your own bootstrap class that uses BasicBootstrap and BasicXMLDeployer to provide whatever functionality you need.</para>
+ <para>Because the deploy() and undeploy() methods of the StandaloneBootstrap are protected
+ you will not be able to call them from your application after the initial startup. If you
+ wish to deploy and undeploy beans at runtime then the EmbeddedBootstrap class may be a
+ better choice. Alternatively you are free to create your own bootstrap class that uses
+ BasicBootstrap and BasicXMLDeployer to provide whatever functionality you need.</para>
</note>
</chapter>
<chapter>
<title>Configuring POJOs</title>
- <para>In accordance with modern development methods JBoss Microcontainer supports POJO configuration via annotations and/or deployment descriptors. The decision to use one or the other depends on what you are trying to achieve. If there is a sensible default configuration for your POJOs then it may be worthwhile defining this using annotations so that users do not have to maintain a separate deployment descriptor. Since annotations are overriden by deployment descriptors at runtime it is always possible for users to change the default configuration using a descriptor if necessary.</para>
+ <para>In accordance with modern development methods JBoss Microcontainer supports POJO
+ configuration via annotations and/or deployment descriptors. The decision to use one or the
+ other depends on what you are trying to achieve. If there is a sensible default
+ configuration for your POJOs then it may be worthwhile defining this using annotations so
+ that users do not have to maintain a separate deployment descriptor. Since annotations are
+ overriden by deployment descriptors at runtime it is always possible for users to change the
+ default configuration using a descriptor if necessary.</para>
<important>
<para>Annotations are overriden by deployment descriptors at runtime.</para>
</important>
- <para>If there is no sensible default configuration then it may be worthwhile using a deployment descriptor instead of annotations. This keeps the source code free of potentially redundant information and allows for easy customization since the initial deployment descriptor acts as a template for further changes.</para>
- <para>Sometimes a mixture of annotations and a deployment descriptor is best. It's up to you to discover what makes most sense for your particular situation. </para>
+ <para>If there is no sensible default configuration then it may be worthwhile using a
+ deployment descriptor instead of annotations. This keeps the source code free of potentially
+ redundant information and allows for easy customization since the initial deployment
+ descriptor acts as a template for further changes.</para>
+ <para>Sometimes a mixture of annotations and a deployment descriptor is best. It's up to
+ you to discover what makes most sense for your particular situation. </para>
<section>
<title>Using annotations</title>
- <para>With the notable exception of defining classloaders nearly all of the microcontainer features can be used by adding annotations to your POJO classes. </para>
+ <para>With the notable exception of defining classloaders nearly all of the microcontainer
+ features can be used by adding annotations to your POJO classes. </para>
<note>
- <para>We are not able to define classloaders using annotations for the simple reason that in order to read any annotations we must first load the class. Once the class is loaded we cannot change its classloader so a classloader annotation would be useless.</para>
+ <para>We are not able to define classloaders using annotations for the simple reason that
+ in order to read any annotations we must first load the class. Once the class is loaded
+ we cannot change its classloader so a classloader annotation would be useless.</para>
</note>
- <para>The microcontainer annotations live in the <code>org.jboss.beans.metadata.api.annotations</code> package and to use them you need Java 1.5 or above.</para>
- <para>Further JBoss Microcontainer allows you to define a subset of the annotations in the XML deployment descriptor. The caveat is that these apply to bean instances instead of bean classes. As with normal annotations you are free to add as many as necessary at a single point. However you must always specify the fully-qualified class name since there is no way to 'import' packages in XML. Currently you can place annotations inside the following elements:</para>
+ <para>The microcontainer annotations live in the
+ <code>org.jboss.beans.metadata.api.annotations</code> package and to use them you need
+ Java 1.5 or above.</para>
+ <para>Further JBoss Microcontainer allows you to define a subset of the annotations in the
+ XML deployment descriptor. The caveat is that these apply to bean instances instead of
+ bean classes. As with normal annotations you are free to add as many as necessary at a
+ single point. However you must always specify the fully-qualified class name since there
+ is no way to 'import' packages in XML. Currently you can place annotations
+ inside the following elements:</para>
<itemizedlist>
<listitem>
<para><deployment> -all beans in the deployment inherit this annotation </para>
@@ -1156,11 +1798,16 @@
<important>
<para>Annotations declared in XML apply to bean instances instead of bean classes.</para>
</important>
- <para>Examples of how to use both normal and XML annotations will be given in the following sections.</para>
+ <para>Examples of how to use both normal and XML annotations will be given in the following
+ sections.</para>
</section>
<section>
<title>Writing deployment descriptors</title>
- <para>Deployment descriptors externalize the configuration of POJOs into one or more files that are separate from the source code. This means that changes can be made without having to recompile any classes. The usual way to define a deployment descriptor is using the bean-deployer XML schema whose root element is <deployment>. This is used to group multiple beans together so that they are deployed as a unit:</para>
+ <para>Deployment descriptors externalize the configuration of POJOs into one or more files
+ that are separate from the source code. This means that changes can be made without having
+ to recompile any classes. The usual way to define a deployment descriptor is using the
+ bean-deployer XML schema whose root element is <deployment>. This is used to group
+ multiple beans together so that they are deployed as a unit:</para>
<programlisting role="XML"><deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:jboss:bean-deployer bean-deployer_2_0.xsd"
xmlns="urn:jboss:bean-deployer:2.0">
@@ -1168,7 +1815,9 @@
<!-- Bean definitions -->
</deployment></programlisting>
- <para>The bean definitions are created using <bean> elements that each describe a single POJO instance. Each bean is given a name so that it can be referenced elsewhere in the deployment descriptor or looked up by a client at runtime.</para>
+ <para>The bean definitions are created using <bean> elements that each describe a
+ single POJO instance. Each bean is given a name so that it can be referenced elsewhere in
+ the deployment descriptor or looked up by a client at runtime.</para>
<programlisting role="XML"><deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:jboss:bean-deployer bean-deployer_2_0.xsd"
xmlns="urn:jboss:bean-deployer:2.0">
@@ -1178,15 +1827,27 @@
<bean name="Address" class="org.jboss.example.microcontainer.AddressBean"/>
</deployment> </programlisting>
- <para>The name of the file containing these elements can be anything you like providing that a deployer can access it and parse the contents correctly. By convention it is called jboss-beans.xml as anything that ends in -beans.xml is found by default using the FileStructure deployer.</para>
+ <para>The name of the file containing these elements can be anything you like providing that
+ a deployer can access it and parse the contents correctly. By convention it is called
+ jboss-beans.xml as anything that ends in -beans.xml is found by default using the
+ FileStructure deployer.</para>
<note>
- <para>Although deployment descriptors are usually created using the bean-deployer XML schema it is also possible to use other XML schemas or even Java properties as shown in the following sections.</para>
+ <para>Although deployment descriptors are usually created using the bean-deployer XML
+ schema it is also possible to use other XML schemas or even Java properties as shown in
+ the following sections.</para>
</note>
</section>
<section>
<title>Using JavaBean XML</title>
- <para>Together with the bean-deployer XML schema the microcontainer also contains a javabean XML schema. This is a lightweight version of bean-deployer that allows for the construction and configuration of <ulink url="http://java.sun.com/products/javabeans/">JavaBeans</ulink> without any advanced features like bean injection, callbacks or dependencies. It is intended to be used in situations where the microcontainer has been customised to remove all of the advanced functionality so that it can be bootstrapped in very small runtime environments. </para>
- <para>Construction of POJOs is performed using either a normal constructor or a static factory method. In either case constructor or method parameters can be added as necessary: </para>
+ <para>Together with the bean-deployer XML schema the microcontainer also contains a javabean
+ XML schema. This is a lightweight version of bean-deployer that allows for the
+ construction and configuration of <ulink url="http://java.sun.com/products/javabeans/"
+ >JavaBeans</ulink> without any advanced features like bean injection, callbacks or
+ dependencies. It is intended to be used in situations where the microcontainer has been
+ customised to remove all of the advanced functionality so that it can be bootstrapped in
+ very small runtime environments. </para>
+ <para>Construction of POJOs is performed using either a normal constructor or a static
+ factory method. In either case constructor or method parameters can be added as necessary: </para>
<itemizedlist>
<listitem>
<para>Default constructor</para>
@@ -1225,9 +1886,12 @@
</listitem>
</itemizedlist>
<note>
- <para>JavaBean declarations do not include a <code>name</code> attribute which means that you cannot inject references into other JavaBeans or lookup a reference to a JavaBean instance from a client at runtime.</para>
+ <para>JavaBean declarations do not include a <code>name</code> attribute which means that
+ you cannot inject references into other JavaBeans or lookup a reference to a JavaBean
+ instance from a client at runtime.</para>
</note>
- <para>Once a JavaBean has been constructed then simple injection of property values is possible using the <property> element: </para>
+ <para>Once a JavaBean has been constructed then simple injection of property values is
+ possible using the <property> element: </para>
<programlisting role="XML"><javabean xmlns="urn:jboss:javabean:2.0"
class="org.jboss.test.javabean.support.SimpleBean">
<property name="ADouble">3.14e12</property>
@@ -1236,13 +1900,20 @@
<property name="ABigInteger">123456</property>
<property name="abyte">12</property>
</javabean></programlisting>
- <para>As with properties defined using the bean-deployer XML schema the string values are converted into objects of the correct type using JavaBean <ulink url="http://java.sun.com/j2se/1.5.0/docs/api/java/beans/PropertyEditor.html">PropertyEditors</ulink>. For cases where the type is ambiguous, e.g. when defining numbers, you can specify the required type by including a <code>class</code> attribute: </para>
+ <para>As with properties defined using the bean-deployer XML schema the string values are
+ converted into objects of the correct type using JavaBean <ulink
+ url="http://java.sun.com/j2se/1.5.0/docs/api/java/beans/PropertyEditor.html"
+ >PropertyEditors</ulink>. For cases where the type is ambiguous, e.g. when defining
+ numbers, you can specify the required type by including a <code>class</code> attribute: </para>
<programlisting role="XML"><javabean xmlns="urn:jboss:javabean:2.0"
class="org.jboss.test.javabean.support.SimpleBean">
<property name="ANumber" class="java.lang.Long">12345</property>
</javabean></programlisting>
<important>
- <para>Beans created using the bean-deployer XML schema can include JavaBeans created using the javabean XML schema instead of string values. This provides a simple way to inject objects composed of multiple values into a bean property without having to write a complicated property editor:</para>
+ <para>Beans created using the bean-deployer XML schema can include JavaBeans created using
+ the javabean XML schema instead of string values. This provides a simple way to inject
+ objects composed of multiple values into a bean property without having to write a
+ complicated property editor:</para>
<programlisting role="XML"><bean name="example" class="org.jboss.acme.Example">
<property name="myProperty">
<javabean xmlns="urn:jboss:javabean:1.0"
@@ -1256,8 +1927,15 @@
</section>
<section>
<title>Using Spring XML</title>
- <para>Spring like JBoss Microcontainer allows POJOs to be created and configured using an XML schema. As the microcontainer largely supports the same set of features as Spring it is possible to use the spring-beans schema to define beans instead of the bean-deployer schema. This makes migrating services built and tested using Spring to JBoss Microcontainer very straightforward.</para>
- <para>To define beans using the spring-beans schema you need to include <code>spring-int.jar</code> on the microcontainer classpath. You can then create a deployment descriptor containing a <beans> element at the root instead of the usual <deployment> element:</para>
+ <para>Spring like JBoss Microcontainer allows POJOs to be created and configured using an
+ XML schema. As the microcontainer largely supports the same set of features as Spring it
+ is possible to use the spring-beans schema to define beans instead of the bean-deployer
+ schema. This makes migrating services built and tested using Spring to JBoss
+ Microcontainer very straightforward.</para>
+ <para>To define beans using the spring-beans schema you need to include
+ <code>spring-int.jar</code> on the microcontainer classpath. You can then create a
+ deployment descriptor containing a <beans> element at the root instead of the usual
+ <deployment> element:</para>
<programlisting role="XML"><beans xmlns="urn:jboss:spring-beans:2.0">
<bean id="testBean" class="org.jboss.test.spring.support.SimpleBean">
<constructor-arg index="2">
@@ -1298,8 +1976,12 @@
</property>
</bean>
</beans></programlisting>
- <para>The namespace declaration <code>xmlns="urn:jboss:spring-beans:2.0"</code> tells the microcontainer that it should interpret the XML according to the spring-bean schema.</para>
- <para>You can even mix beans declared using the bean-deployer schema with those declared using the spring-beans schema just like we did with the javabean schema. All that you need to do is include the correct namespace declarations in the relevant elements:</para>
+ <para>The namespace declaration <code>xmlns="urn:jboss:spring-beans:2.0"</code>
+ tells the microcontainer that it should interpret the XML according to the spring-bean
+ schema.</para>
+ <para>You can even mix beans declared using the bean-deployer schema with those declared
+ using the spring-beans schema just like we did with the javabean schema. All that you need
+ to do is include the correct namespace declarations in the relevant elements:</para>
<itemizedlist>
<listitem>
<para>JBoss Microcontainer deployment containing Spring defined beans</para>
@@ -1342,22 +2024,35 @@
</listitem>
</itemizedlist>
<note>
- <para>Adding <code>spring-int.jar</code> to the microcontainer classpath simply adds the ability to define beans using the spring-beans XML schema. It does not integrate JBoss Microcontainer with Spring libraries.</para>
+ <para>Adding <code>spring-int.jar</code> to the microcontainer classpath simply adds the
+ ability to define beans using the spring-beans XML schema. It does not integrate JBoss
+ Microcontainer with Spring libraries.</para>
</note>
</section>
<section>
<title>Using Java Properties</title>
- <para>Using XML schemas to create deployment descriptors provides us wth a powerful way to express complex configuration information. However the XML parsers that are needed to interpret this information are often large in size which doesn't help when bootstrapping the microcontainer in small runtime environments. For this reason you can also choose to create deployment descriptors using Java properties.</para>
+ <para>Using XML schemas to create deployment descriptors provides us wth a powerful way to
+ express complex configuration information. However the XML parsers that are needed to
+ interpret this information are often large in size which doesn't help when
+ bootstrapping the microcontainer in small runtime environments. For this reason you can
+ also choose to create deployment descriptors using Java properties.</para>
</section>
</chapter>
<chapter>
<title>Creating POJOs</title>
<section>
<title>Defining classloaders</title>
- <para>In order to create POJO instances we must first load the corresponding POJO classes into the JVM using a classloader. JBoss Microcontainer does this by using the classloader associated with the current thread of execution, i.e. the thread context classloader. But what do we mean by the current thread of execution and who sets its context classloader?</para>
- <para>In all of our examples so far we have created applications that only use a single thread. This is the 'primordial' thread created by the main() function:</para>
+ <para>In order to create POJO instances we must first load the corresponding POJO classes
+ into the JVM using a classloader. JBoss Microcontainer does this by using the classloader
+ associated with the current thread of execution, i.e. the thread context classloader. But
+ what do we mean by the current thread of execution and who sets its context
+ classloader?</para>
+ <para>In all of our examples so far we have created applications that only use a single
+ thread. This is the 'primordial' thread created by the main() function:</para>
<programlisting role="JAVA">public static void main(String[] args) {}</programlisting>
- <para>As the only thread in the application (other than the garbage collecton thread which is created for every Java application) this executes all of the code including calling the deploy() method of our BasicXMLDeployer:</para>
+ <para>As the only thread in the application (other than the garbage collecton thread which
+ is created for every Java application) this executes all of the code including calling the
+ deploy() method of our BasicXMLDeployer:</para>
<programlisting role="JAVA">public class BasicXMLDeployer extends BasicKernelDeployer
{
...
@@ -1392,18 +2087,53 @@
...
}</programlisting>
- <para>The deploy() method takes a URL representing the location of an XML deployment descriptor and passes it as a string to an unmarshaller. The unmarshaller parses the XML, converts it into an object representation called a KernelDeployment and sets its name. The KernelDeployment object is then passed to the deploy() method of the superclass BasicKernelDeployer. This iterates through all of the beans in the deployment creating a context for each one which it subsequently passes to the contoller's install() method to peform the deployment. The controller then performs actions on each context to take the bean from one state to another: NOT_INSTALLED, DESCRIBED, CONFIGURED, INSTANTIATED, CREATE, START, INSTALLED. Once a bean reaches the INSTALLED state then it's considered to be deployed.</para>
+ <para>The deploy() method takes a URL representing the location of an XML deployment
+ descriptor and passes it as a string to an unmarshaller. The unmarshaller parses the XML,
+ converts it into an object representation called a KernelDeployment and sets its name. The
+ KernelDeployment object is then passed to the deploy() method of the superclass
+ BasicKernelDeployer. This iterates through all of the beans in the deployment creating a
+ context for each one which it subsequently passes to the contoller's install() method
+ to peform the deployment. The controller then performs actions on each context to take the
+ bean from one state to another: NOT_INSTALLED, DESCRIBED, CONFIGURED, INSTANTIATED,
+ CREATE, START, INSTALLED. Once a bean reaches the INSTALLED state then it's
+ considered to be deployed.</para>
<important>
- <para>During the deployment process, as the controller performs the Instantiate action, the thread context classloader is used to load the bean's class into the JVM. As all of the controller code is executed by the thread that calls controller.install() we refer to this as the current thread of execution.</para>
+ <para>During the deployment process, as the controller performs the Instantiate action,
+ the thread context classloader is used to load the bean's class into the JVM. As
+ all of the controller code is executed by the thread that calls controller.install() we
+ refer to this as the current thread of execution.</para>
</important>
- <para>Therefore when using the BasicXMLDeployer the classloader used to load bean classes is retrieved from the thread that calls the BasicXMLDeployer's deploy() method as this goes on to call controller.install() which subsequently executes the Instantiate action. For all of our examples this is the primordial thread whose context classloader is set on startup to the classloader that loaded the application, i.e. the application classloader. This means that in our examples the microcontainer can load any bean classes present on the application, extension or boot classpaths.</para>
- <para>What happens though if we create our own threads in the application and use these to call the deploy() method of our BasicXMLDeployer?</para>
- <para>If you create a thread then by default its context classloader will be set to the parent thread's context classloader. As any hierarchy of threads is ultimately rooted at the primordial thread this means that by default they will all use the application classloader. To change this you simply need to call setContextClassLoader() on a thread at runtime. This is typically done on the current thread using <code>Thread.currentThread().setContextClassloader()</code>.</para>
+ <para>Therefore when using the BasicXMLDeployer the classloader used to load bean classes is
+ retrieved from the thread that calls the BasicXMLDeployer's deploy() method as this
+ goes on to call controller.install() which subsequently executes the Instantiate action.
+ For all of our examples this is the primordial thread whose context classloader is set on
+ startup to the classloader that loaded the application, i.e. the application classloader.
+ This means that in our examples the microcontainer can load any bean classes present on
+ the application, extension or boot classpaths.</para>
+ <para>What happens though if we create our own threads in the application and use these to
+ call the deploy() method of our BasicXMLDeployer?</para>
+ <para>If you create a thread then by default its context classloader will be set to the
+ parent thread's context classloader. As any hierarchy of threads is ultimately rooted
+ at the primordial thread this means that by default they will all use the application
+ classloader. To change this you simply need to call setContextClassLoader() on a thread at
+ runtime. This is typically done on the current thread using
+ <code>Thread.currentThread().setContextClassloader()</code>.</para>
<note>
- <para>If you choose not to use the BasicXMLDeployer and instead use the equivalent aspectized deployer then the classloader used to load bean classes is taken from the thread that calls the process() method of the MainDeployer. This is because the MainDeployer process() method iterates through all of the registered deployers calling their process() methods which in turn call controller.install() to perform the deployment. </para>
+ <para>If you choose not to use the BasicXMLDeployer and instead use the equivalent
+ aspectized deployer then the classloader used to load bean classes is taken from the
+ thread that calls the process() method of the MainDeployer. This is because the
+ MainDeployer process() method iterates through all of the registered deployers calling
+ their process() methods which in turn call controller.install() to perform the
+ deployment. </para>
</note>
- <para>Now that we know the default classloader comes from the current thread of execution, which if not set explicitly is set on startup to be the application classloader, how can we change it to load bean classes from other locations?</para>
- <para>If you are using the BasicXMLDeployer or its equivalent aspectized deployer then you can define classloaders for entire deployments or individual beans by including <classloader> elements within the XML deployment descriptor. To specify a different classloader for all the beans in a deployment you need to include a <classloader> element above all of the <bean> elements:</para>
+ <para>Now that we know the default classloader comes from the current thread of execution,
+ which if not set explicitly is set on startup to be the application classloader, how can
+ we change it to load bean classes from other locations?</para>
+ <para>If you are using the BasicXMLDeployer or its equivalent aspectized deployer then you
+ can define classloaders for entire deployments or individual beans by including
+ <classloader> elements within the XML deployment descriptor. To specify a different
+ classloader for all the beans in a deployment you need to include a <classloader>
+ element above all of the <bean> elements:</para>
<programlisting role="XML"><deployment>
<classloader><inject bean="deploymentCL"/></classloader>
@@ -1416,7 +2146,9 @@
</bean>
</deployment></programlisting>
- <para>If instead you wish to override either the default classloader or a deployment classloader for a particular bean then you need to include a <classloader> element within the <bean> definition itself:</para>
+ <para>If instead you wish to override either the default classloader or a deployment
+ classloader for a particular bean then you need to include a <classloader> element
+ within the <bean> definition itself:</para>
<programlisting role="XML"><deployment>
<classloader><inject bean="deploymentCL"/></classloader>
@@ -1430,7 +2162,9 @@
</bean>
</deployment></programlisting>
- <para>Finally you can choose to use the default classloader for a bean instead of the deployment classloader, if one is defined, by including a <classloader> element with a </null> value:</para>
+ <para>Finally you can choose to use the default classloader for a bean instead of the
+ deployment classloader, if one is defined, by including a <classloader> element with
+ a </null> value:</para>
<programlisting role="XML"><deployment>
<classloader><inject bean="deploymentCL"/></classloader>
@@ -1445,7 +2179,9 @@
</bean>
</deployment></programlisting>
- <para>The classloader beans that you inject must be deployed in the runtime environment before bean classes can be loaded. Typically this is done by also declaring them in the XML deployment descriptor:</para>
+ <para>The classloader beans that you inject must be deployed in the runtime environment
+ before bean classes can be loaded. Typically this is done by also declaring them in the
+ XML deployment descriptor:</para>
<programlisting role="XML"><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">
@@ -1470,16 +2206,36 @@
<classloader><inject bean="customCL"/></classloader>
...
</bean></programlisting>
- <para>If a classloader is not available for a bean when an attempt is made to deploy it then the bean will remain in a pre INSTANTIATED state. Once the classloader is deployed then the bean will automatically continue to deploy. This means that you can define classloaders in the deployment descriptor after beans that depend on them and everything will deploy as expected.</para>
+ <para>If a classloader is not available for a bean when an attempt is made to deploy it then
+ the bean will remain in a pre INSTANTIATED state. Once the classloader is deployed then
+ the bean will automatically continue to deploy. This means that you can define
+ classloaders in the deployment descriptor after beans that depend on them and everything
+ will deploy as expected.</para>
<note>
- <para>User defined classloaders are detected by the microcontainer during the deployment process as the controller performs the Instantiate action. They are used instead of the default classloader by calling setContextClassLoader() on the current thread. In this way they can be subsequently retrieved by code that loads the bean class using Thread.currentThread().getContextClassLoader(). After the bean class has been loaded the default classloader is put back by calling setContexClassLoader() again.</para>
+ <para>User defined classloaders are detected by the microcontainer during the deployment
+ process as the controller performs the Instantiate action. They are used instead of the
+ default classloader by calling setContextClassLoader() on the current thread. In this
+ way they can be subsequently retrieved by code that loads the bean class using
+ Thread.currentThread().getContextClassLoader(). After the bean class has been loaded the
+ default classloader is put back by calling setContexClassLoader() again.</para>
</note>
</section>
<section>
<title>Calling constructors</title>
- <para>Creating POJO instances is performed either by calling a constructor or using a factory method. The <constructor> element is used in both cases and parameters can be passed if necessary using nested <parameter> elements. Annotations also exist for either case with parameter values represented as annotations of the constructor parameters themselves.</para>
+ <para>Creating POJO instances is performed either by calling a constructor or using a
+ factory method. The <constructor> element is used in both cases and parameters can
+ be passed if necessary using nested <parameter> elements. Annotations also exist for
+ either case with parameter values represented as annotations of the constructor parameters
+ themselves.</para>
<important>
- <para>At present it is not possible to create a POJO instance only using annotations. This is because classes are not automatically loaded and scanned whenever they are added to the classpath. Doing so would significantly reduce the performance of the deployment process as all classes would need to be loaded, even if they were not yet required. Instead you must always define beans using a deployment descriptor as this tells the microcontainer which classes to load. These classes are subsequently scanned for annotations such as @Constructor and @Factory to discover how to create instances.</para>
+ <para>At present it is not possible to create a POJO instance only using annotations. This
+ is because classes are not automatically loaded and scanned whenever they are added to
+ the classpath. Doing so would significantly reduce the performance of the deployment
+ process as all classes would need to be loaded, even if they were not yet required.
+ Instead you must always define beans using a deployment descriptor as this tells the
+ microcontainer which classes to load. These classes are subsequently scanned for
+ annotations such as @Constructor and @Factory to discover how to create
+ instances.</para>
<programlisting role="XML"><bean name="SimpleBean" class="org.jboss.example.SimpleBean"/></programlisting>
</important>
<itemizedlist>
@@ -1521,11 +2277,16 @@
</constructor>
</bean></programlisting>
<note>
- <para>You only need to include the <code>class</code> attribute on enough parameters to resolve the ambiguity.</para>
+ <para>You only need to include the <code>class</code> attribute on enough parameters
+ to resolve the ambiguity.</para>
</note>
</listitem>
</itemizedlist>
- <para>You can also specify javabeans as parameters if required using the @JavaBeanValue annotation or <javabean> element. In this particular case the annotation is not very useful as it can only call the default constructor of the javabean class to create an instance. If you want to use a different constructor or inject properties then you should use the XML element instead.</para>
+ <para>You can also specify javabeans as parameters if required using the @JavaBeanValue
+ annotation or <javabean> element. In this particular case the annotation is not very
+ useful as it can only call the default constructor of the javabean class to create an
+ instance. If you want to use a different constructor or inject properties then you should
+ use the XML element instead.</para>
<programlisting role="JAVA">@Constructor
public TestConstructorBean(@StringValue("this is a test constructor") String testString, @JavaBeanValueorg.jboss.test.Person.class) Person testPerson) {
...
@@ -1544,7 +2305,8 @@
</parameter>
</constructor>
</bean></programlisting>
- <para>Finally you can inject references to other beans or their properties into constructor parameters giving you complete control of the construction of your POJOs.</para>
+ <para>Finally you can inject references to other beans or their properties into constructor
+ parameters giving you complete control of the construction of your POJOs.</para>
<programlisting role="JAVA">@Constructor
public TestConstructorBean(@Inject(bean="TestPerson") Person testPerson, @Inject(bean="TestAddress", property="street") String street) {
...
@@ -1562,7 +2324,9 @@
</section>
<section>
<title>Using factories</title>
- <para>If you have a number of classes that share the same interface then you may want to use a factory to create your POJO instances. JBoss Microcontainer provides support for static or non-static factory methods which can also take parameters if necessary.</para>
+ <para>If you have a number of classes that share the same interface then you may want to use
+ a factory to create your POJO instances. JBoss Microcontainer provides support for static
+ or non-static factory methods which can also take parameters if necessary.</para>
<itemizedlist>
<listitem>
<para>Static factory method</para>
@@ -1594,7 +2358,8 @@
</bean> </programlisting>
</listitem>
<listitem>
- <para>Non-static factory method (instance of factory created as JavaBean when needed)</para>
+ <para>Non-static factory method (instance of factory created as JavaBean when
+ needed)</para>
<programlisting role="JAVA">@Factory (
factory=@Value(javabean=@JavaBeanValue(org.jboss.example.MyOtherFactory.class)),
factoryMethod="createBean")
@@ -1611,14 +2376,14 @@
</bean></programlisting>
</listitem>
<listitem>
- <para>Non-static factory method with parameters (instance of factory installed as a Microcontainer bean)</para>
+ <para>Non-static factory method with parameters (instance of factory installed as a
+ Microcontainer bean)</para>
<programlisting role="JAVA">@Factory (
factory=@Value(inject=@Inject(bean="Factory"))),
factoryMethod="createBean",
parameters={@Value(string=@StringValue("a string")),
@Value(string=@StringValue("7"))})
public class SimpleBean {</programlisting>
-
<programlisting role="XML"><bean name="MyOtherFactory" class="org.jboss.example.MyOtherFactory"/>
<bean name="SimpleBean" class="org.jboss.example.SimpleBean">
@@ -1630,7 +2395,8 @@
</bean></programlisting>
</listitem>
</itemizedlist>
- <para>For the special case where a bean implements its own static factory method in order to create itself then you can also use a shorthand notation:</para>
+ <para>For the special case where a bean implements its own static factory method in order to
+ create itself then you can also use a shorthand notation:</para>
<itemizedlist>
<listitem>
<para>Bean implementing its own static factory method</para>
@@ -1647,16 +2413,23 @@
<parameter>5</parameter>
</constructor>
</bean> </programlisting>
- <para>Note that the @FactoryMethod annotation doesn't have a parameter attribute as the method parameters themselves are annotated.</para>
+ <para>Note that the @FactoryMethod annotation doesn't have a parameter attribute as
+ the method parameters themselves are annotated.</para>
</listitem>
</itemizedlist>
<note>
- <para>Factory method parameters can also be configured using JavaBeans and injected bean references if necessary just like constructor parameters. </para>
+ <para>Factory method parameters can also be configured using JavaBeans and injected bean
+ references if necessary just like constructor parameters. </para>
</note>
</section>
<section>
<title>Creating bean aliases</title>
- <para>By default each bean declared in the deployment descriptor has a unique name that is used to inject it into other beans or to provide a reference to clients at runtime. Sometimes however it is useful to define additional names or aliases. This can be done using the @Aliases annotation or <alias> XML element. System property replacement is used by default to change the value of the alias at runtime but you can disable this if required. </para>
+ <para>By default each bean declared in the deployment descriptor has a unique name that is
+ used to inject it into other beans or to provide a reference to clients at runtime.
+ Sometimes however it is useful to define additional names or aliases. This can be done
+ using the @Aliases annotation or <alias> XML element. System property replacement is
+ used by default to change the value of the alias at runtime but you can disable this if
+ required. </para>
<itemizedlist>
<listitem>
<para>Defining extra bean names</para>
@@ -1681,7 +2454,9 @@
</bean></programlisting>
</listitem>
</itemizedlist>
- <para>The type of an alias doesn't have to be a string. If you want to choose a different type then you can specify a <code>class</code> attribute in the <alias> XML element. At present you cannot do this using the @Aliases annotation.</para>
+ <para>The type of an alias doesn't have to be a string. If you want to choose a
+ different type then you can specify a <code>class</code> attribute in the <alias>
+ XML element. At present you cannot do this using the @Aliases annotation.</para>
<itemizedlist>
<listitem>
<para>Choosing a different alias type</para>
@@ -1690,7 +2465,9 @@
</bean></programlisting>
</listitem>
</itemizedlist>
- <para>Sometimes you may wish to define an alias for a bean after it has been deployed. This can be done using the standalone <alias> element in the deployment descriptor. There is no equivalent annotation in this case as there is no sensible place to put it.</para>
+ <para>Sometimes you may wish to define an alias for a bean after it has been deployed. This
+ can be done using the standalone <alias> element in the deployment descriptor. There
+ is no equivalent annotation in this case as there is no sensible place to put it.</para>
<itemizedlist>
<listitem>
<para>Defining a standalone alias</para>
@@ -1715,8 +2492,14 @@
</listitem>
</itemizedlist>
<note>
- <para>Standalone aliases behave just like normal beans with respect to dependencies. Specifically they won't be deployed until the beans that they represent have been deployed first. Likewise when a bean is undeployed then any corresponding standalone aliases are undeployed first.</para>
- <para>Furthermore if any beans depend on a standalone alias then they will not be deployed until the alias is deployed first. Similarly undeploying a standalone alias will cause all dependent beans to be undeployed, regardless of whether the bean that it represents remains deployed. </para>
+ <para>Standalone aliases behave just like normal beans with respect to dependencies.
+ Specifically they won't be deployed until the beans that they represent have been
+ deployed first. Likewise when a bean is undeployed then any corresponding standalone
+ aliases are undeployed first.</para>
+ <para>Furthermore if any beans depend on a standalone alias then they will not be deployed
+ until the alias is deployed first. Similarly undeploying a standalone alias will cause
+ all dependent beans to be undeployed, regardless of whether the bean that it represents
+ remains deployed. </para>
</note>
</section>
</chapter>
@@ -1724,7 +2507,10 @@
<title>Injecting properties</title>
<section>
<title>Using values</title>
- <para>Once a POJO instance has been created then it can be configured by injecting property values. Like most other features this can be done using either annotations or a deployment descriptor. For each property that you wish to inject the bean must define an appropriate setter method for the microcontainer to call.</para>
+ <para>Once a POJO instance has been created then it can be configured by injecting property
+ values. Like most other features this can be done using either annotations or a deployment
+ descriptor. For each property that you wish to inject the bean must define an appropriate
+ setter method for the microcontainer to call.</para>
<programlisting role="JAVA">public class PropertiesBean {
public void setTitle(@StringValue("JBoss Microcontainer") String title) {
@@ -1745,7 +2531,13 @@
<property name="link">http://www.jboss.org</property>
<property name="number">4</property>
</bean> </programlisting>
- <para>As you have probably noticed we are required to enter all of our values as strings since we are dealing with text files. These strings are converted into objects of the correct type using JavaBean <ulink url="http://java.sun.com/j2se/1.5.0/docs/api/java/beans/PropertyEditor.html">PropertyEditors</ulink>. If the property takes an interface or abstract class then you can pass a specific implementation using a <code>type</code> or <code>class</code> attribute depending on whether you're using annotations or XML.</para>
+ <para>As you have probably noticed we are required to enter all of our values as strings
+ since we are dealing with text files. These strings are converted into objects of the
+ correct type using JavaBean <ulink
+ url="http://java.sun.com/j2se/1.5.0/docs/api/java/beans/PropertyEditor.html"
+ >PropertyEditors</ulink>. If the property takes an interface or abstract class then you
+ can pass a specific implementation using a <code>type</code> or <code>class</code>
+ attribute depending on whether you're using annotations or XML.</para>
<programlisting role="JAVA">public class PropertiesBean {
public void setNumber(@StringValue(value="4", type=java.lang.Long.class) Number number) {
...
@@ -1755,7 +2547,11 @@
class="org.jboss.example.microcontainer.properties.PropertiesBean">
<property name="number" class="java.lang.Long">4</property>
</bean> </programlisting>
- <para>Sometimes you may want to pass in a value that isn't of the same type as the property but can be progressed into it. Progression is typically used for converting numbers from one type to another, e.g. Short to Int or Float to Double. JBoss Microcontainer by default will automatically perform progression using a <code>SimpleProgressionConverter</code> implementation.</para>
+ <para>Sometimes you may want to pass in a value that isn't of the same type as the
+ property but can be progressed into it. Progression is typically used for converting
+ numbers from one type to another, e.g. Short to Int or Float to Double. JBoss
+ Microcontainer by default will automatically perform progression using a
+ <code>SimpleProgressionConverter</code> implementation.</para>
<programlisting role="XML"><bean name="SimpleBean" class="org.jboss.test.SimpleBean">
<property name="anInt">
<javabean xmlns="urn:jboss:javabean:2.0" class="java.lang.Double">
@@ -1779,9 +2575,15 @@
</javabean>
</property>
</bean></programlisting>
- <para>You can replace the default progression converter implementation with one of your own by setting the <code>org.jboss.reflect.plugins.progressionConverter</code> system property using a fully-qualified class name. If you want to prevent progression from occuring altogether then you can use the <code>NullProgressionConverter</code> which is also provided.</para>
+ <para>You can replace the default progression converter implementation with one of your own
+ by setting the <code>org.jboss.reflect.plugins.progressionConverter</code> system property
+ using a fully-qualified class name. If you want to prevent progression from occuring
+ altogether then you can use the <code>NullProgressionConverter</code> which is also
+ provided.</para>
<programlisting>-Dorg.jboss.reflect.plugins.progressionConverter=org.jboss.reflect.plugins.NullProgressionConvertor</programlisting>
- <para>If you want to use a null value then you need to use the @NullValue annotation or <null/> element. This is so we can differentiate it from the string 'null'.</para>
+ <para>If you want to use a null value then you need to use the @NullValue annotation or
+ <null/> element. This is so we can differentiate it from the string
+ 'null'.</para>
<programlisting role="JAVA">public void setTitle(@NullValue String title) {
...
} </programlisting>
@@ -1789,7 +2591,8 @@
class="org.jboss.example.microcontainer.properties.PropertiesBean">
<property name="title"><null/></property>
</bean> </programlisting>
- <para>Similarly if you want to refer to the current object then you should use the @ThisValue annotation or <this/> element.</para>
+ <para>Similarly if you want to refer to the current object then you should use the
+ @ThisValue annotation or <this/> element.</para>
<programlisting role="JAVA">public void setObject(@ThisValue Object target) {
...
} </programlisting>
@@ -1804,7 +2607,9 @@
<value class="java.lang.Long">4</value>
</property>
</bean> </programlisting>
- <para>This is primarily used when configuring values representing collections such as lists, sets and arrays. Elements of a collection are defined by listing annotations or nesting <value> elements as follows:</para>
+ <para>This is primarily used when configuring values representing collections such as lists,
+ sets and arrays. Elements of a collection are defined by listing annotations or nesting
+ <value> elements as follows:</para>
<programlisting role="JAVA">@CollectionValue(elementClass=java.lang.String.class,
{@Value(string=@StringValue("string1")),
@Value(string=@StringValue("string2")),
@@ -1823,7 +2628,9 @@
</collection>
</property>
</bean> </programlisting>
- <para>The <code>elementClass</code> attribute specifies the default class for the values. This can be overriden for individual values using the <code>type</code> or <code>class</code> attributes.</para>
+ <para>The <code>elementClass</code> attribute specifies the default class for the values.
+ This can be overriden for individual values using the <code>type</code> or
+ <code>class</code> attributes.</para>
<programlisting role="JAVA">@CollectionValue(elementClass=java.lang.String.class,
{@Value(string=@StringValue("string1")),
@Value(string=@StringValue("string2")),
@@ -1843,9 +2650,11 @@
</property>
</bean> </programlisting>
<warning>
- <para>The type of each value must be specified using either the <code>elementClass</code> or <code>type/class</code> attribute in order for the collection to be created.</para>
+ <para>The type of each value must be specified using either the <code>elementClass</code>
+ or <code>type/class</code> attribute in order for the collection to be created.</para>
</warning>
- <para>Lists, sets and arrays take the same form as the collection annotation and XML element. </para>
+ <para>Lists, sets and arrays take the same form as the collection annotation and XML
+ element. </para>
<programlisting role="JAVA">@ListValue(elementClass=java.lang.String.class,
{@Value(string=@StringValue("my first string")),
@Value(string=@StringValue("my second string"))})
@@ -1911,7 +2720,10 @@
</list>
</property>
</bean></programlisting>
- <para>Maps can also be created using multiple entries of key/value pairs. As we now have two types to consider we must use the <code>keyClass</code> and <code>valueClass</code> attributes to specify the default classes for each entry. Again these can be overriden for individual entries if necessary.</para>
+ <para>Maps can also be created using multiple entries of key/value pairs. As we now have two
+ types to consider we must use the <code>keyClass</code> and <code>valueClass</code>
+ attributes to specify the default classes for each entry. Again these can be overriden for
+ individual entries if necessary.</para>
<programlisting role="JAVA">@MapValue(keyClass=java.lang.String.class,
valueClass=java.lang.String.class;,
{@EntryValue(key=@Value(string=@StringValue("foo.bar.key")),
@@ -1936,7 +2748,14 @@
</map>
</property>
</bean> </programlisting>
- <para>So far we have defined collections, lists, sets, arrays and maps but all of these have multiple implementations, e.g. java.util.ArrayList, java.util.LinkedList, so which are we using at runtime? By default JBoss Microcontainer will look for an existing instance using the corresponding getter method for the property. If one is found then it is used, and any values defined in the annotations or xml deployment descriptor will be added to the exisiting values. If not then the type of the property is analysed and providing that it represents a class (not an interface) then this is used to create a new instance. Failing that a default instance will be created as follows:</para>
+ <para>So far we have defined collections, lists, sets, arrays and maps but all of these have
+ multiple implementations, e.g. java.util.ArrayList, java.util.LinkedList, so which are we
+ using at runtime? By default JBoss Microcontainer will look for an existing instance using
+ the corresponding getter method for the property. If one is found then it is used, and any
+ values defined in the annotations or xml deployment descriptor will be added to the
+ exisiting values. If not then the type of the property is analysed and providing that it
+ represents a class (not an interface) then this is used to create a new instance. Failing
+ that a default instance will be created as follows:</para>
<itemizedlist>
<listitem>
<para>Collection - java.util.ArrayList</para>
@@ -1954,7 +2773,10 @@
<para>Map - java.util.HashMap</para>
</listitem>
</itemizedlist>
- <para>If you want to override this behaviour and specify your own class then you can simply add a <code>clazz</code> attribute to your annotation or <code>class</code> attribute to your XML element containing the fully-qualified class name. This causes the microcontainer to create a new instance using the specified class.</para>
+ <para>If you want to override this behaviour and specify your own class then you can simply
+ add a <code>clazz</code> attribute to your annotation or <code>class</code> attribute to
+ your XML element containing the fully-qualified class name. This causes the microcontainer
+ to create a new instance using the specified class.</para>
<programlisting role="JAVA">@SetValue(clazz=java.util.TreeSet.class,
elementClass=java.lang.Integer.class,
{@Value(string=@StringValue("50")),
@@ -1972,7 +2794,11 @@
</property>
</bean> </programlisting>
<note>
- <para>If you don't specify a <code>clazz</code><code>/class</code> attribute but you want to ensure that a new instance is created then you can prevent the microcontainer from looking for a existing instance by specifying a <code>preinstantiate</code> attribute in the <property> element. Currently this cannot be achieved using annotations.</para>
+ <para>If you don't specify a <code>clazz</code><code>/class</code> attribute but you
+ want to ensure that a new instance is created then you can prevent the microcontainer
+ from looking for a existing instance by specifying a <code>preinstantiate</code>
+ attribute in the <property> element. Currently this cannot be achieved using
+ annotations.</para>
<programlisting role="XML"><bean name="SimpleBean" class="org.jboss.example.SimpleBean">
<property name="list" preinstantiate="false">
<list elementClass="java.lang.String">
@@ -1983,15 +2809,23 @@
</bean> </programlisting>
</note>
<note>
- <para>If you want to define a <code>clazz</code><code>/class</code> attribute for an array then you must use the following syntax:</para>
- <para> <code>[L</code><emphasis><fully-qualified class name></emphasis><code>;</code></para>
+ <para>If you want to define a <code>clazz</code><code>/class</code> attribute for an array
+ then you must use the following syntax:</para>
+ <para>
+ <code>[L</code><emphasis><fully-qualified class
+ name></emphasis><code>;</code></para>
<para>e.g. an array of strings - [Ljava.lang.String;</para>
<para>or an array of integers - [Ljava.lang.Integer;</para>
</note>
</section>
<section>
<title>Calling methods</title>
- <para>Using PropertyEditors to create instances of objects from strings is fine in many situations but sometimes you need to inject a return value from a method call. As the method is responsible for creating the value we refer to it as a value factory. Hence the @ValueFactory annotation or <value-factory> element can be used in place of @StringValue or <value> to call a method and inject the returned value during deployment.</para>
+ <para>Using PropertyEditors to create instances of objects from strings is fine in many
+ situations but sometimes you need to inject a return value from a method call. As the
+ method is responsible for creating the value we refer to it as a value factory. Hence the
+ @ValueFactory annotation or <value-factory> element can be used in place of
+ @StringValue or <value> to call a method and inject the returned value during
+ deployment.</para>
<programlisting role="JAVA">@ValueFactory(bean="SimpleBean",
method="getFullName")
public void setName(String name) {
@@ -2002,7 +2836,8 @@
<value-factory bean="SimpleBean" method="getFullName"/>
</property>
</bean> </programlisting>
- <para>If you need to pass any parameters to the method then you can use multiple @Parameter annotations or nested <parameter> elements. </para>
+ <para>If you need to pass any parameters to the method then you can use multiple @Parameter
+ annotations or nested <parameter> elements. </para>
<programlisting role="JAVA">@ValueFactory(bean="SimpleBean",
method="getFullName",
parameters={@Parameter(string=@StringValue("Bob")),
@@ -2018,7 +2853,9 @@
</value-factory>
</property>
</bean> </programlisting>
- <para>Alternatively if only one parameter is required then you can use the <code>parameter</code> attribute to create a shorthand version. However this only works for values that can be created from strings.</para>
+ <para>Alternatively if only one parameter is required then you can use the
+ <code>parameter</code> attribute to create a shorthand version. However this only works
+ for values that can be created from strings.</para>
<programlisting role="JAVA">@ValueFactory(bean="SimpleBean",
method="getFullName",
parameter="Bob")
@@ -2030,7 +2867,9 @@
<value-factory bean="SimpleBean" method="getFullName" parameter="Bob"/>
</property>
</bean> </programlisting>
- <para>It is also possible to specify a default value to use in case the method returns null. This is done using the <code>defaultValue</code>/<code>default</code> attribute which like the <code>parameter</code> attribute only accepts values created using strings. </para>
+ <para>It is also possible to specify a default value to use in case the method returns null.
+ This is done using the <code>defaultValue</code>/<code>default</code> attribute which like
+ the <code>parameter</code> attribute only accepts values created using strings. </para>
<programlisting role="JAVA">@ValueFactory(bean="SimpleBean",
method="getFullName",
defaultValue="Mark Newton")
@@ -2045,7 +2884,10 @@
</section>
<section>
<title>Using bean references</title>
- <para>Injecting string-based properties or the return values of method calls into individual beans allows us to perform simple configurations. To create more complex configurations we need to wire beans together by injecting references using the @Inject annotation or <inject> element. </para>
+ <para>Injecting string-based properties or the return values of method calls into individual
+ beans allows us to perform simple configurations. To create more complex configurations we
+ need to wire beans together by injecting references using the @Inject annotation or
+ <inject> element. </para>
<programlisting role="JAVA">@Inject(bean = "Name1")
public void setSimpleBean(SimpleBean bean) {
...
@@ -2055,7 +2897,10 @@
<inject bean="Name1"/>
</property>
</bean></programlisting>
- <para>Bean injections can be used anywhere a string value is used but you must ensure that the type of bean being injected is compatible with the type of property being set. It is also possible to inject a property from one bean into another using the <code>property</code> attribute.</para>
+ <para>Bean injections can be used anywhere a string value is used but you must ensure that
+ the type of bean being injected is compatible with the type of property being set. It is
+ also possible to inject a property from one bean into another using the
+ <code>property</code> attribute.</para>
<programlisting role="JAVA">@Inject(bean = "Name1", property="age")
public void setAge(Integer age) {
...
@@ -2065,9 +2910,14 @@
<inject bean="Name1" property="age"/>
</property>
</bean></programlisting>
- <para>Although injection is simple to use it provides a very powerful way to create arbitrary relationships between POJO instances. The dependencies that are formed by these relationships are discussed in the next chapter 'Defining Dependencies'.</para>
+ <para>Although injection is simple to use it provides a very powerful way to create
+ arbitrary relationships between POJO instances. The dependencies that are formed by these
+ relationships are discussed in the next chapter 'Defining Dependencies'.</para>
<note>
- <para>Sometimes you may wish to declare a bean simply so that you can inject it into a single property. In this case you can replace the <inject> element with the bean declaration itself to reduce the amount of XML required. This is not possible using annotations as there is no annotation to declare a bean.</para>
+ <para>Sometimes you may wish to declare a bean simply so that you can inject it into a
+ single property. In this case you can replace the <inject> element with the bean
+ declaration itself to reduce the amount of XML required. This is not possible using
+ annotations as there is no annotation to declare a bean.</para>
<programlisting role="XML"><bean name="SimpleBean" class="org.jboss.test.SimpleBean">
<property name="testBean">
<bean="TestBean" class="org.jboss.test.TestBean">
@@ -2079,7 +2929,11 @@
</section>
<section>
<title>Injecting context information</title>
- <para>You may recall that inside the microcontainer each bean is represented by a context which holds amongst other things the bean's current state. This context information can be injected into bean properties or constructor/factory-method parameters using the @Inject annotation or <inject> element together with a <code>fromContext</code> attribute.</para>
+ <para>You may recall that inside the microcontainer each bean is represented by a context
+ which holds amongst other things the bean's current state. This context information
+ can be injected into bean properties or constructor/factory-method parameters using the
+ @Inject annotation or <inject> element together with a <code>fromContext</code>
+ attribute.</para>
<programlisting role="JAVA">@Inject(bean="otherBean",
fromContext=FromContext.BEANINFO)
public void setBeanInfo(BeanInfo beanInfo) {
@@ -2090,7 +2944,8 @@
<inject bean="otherBean" fromContext="beaninfo"/>
</property>
</bean></programlisting>
- <para>If you want to inject information about the current bean's context then you can simply omit the <code>bean</code> attribute.</para>
+ <para>If you want to inject information about the current bean's context then you can
+ simply omit the <code>bean</code> attribute.</para>
<programlisting role="JAVA">@Inject(fromContext=FromContext.BEANINFO)
public void setBeanInfo(BeanInfo beanInfo) {
...
@@ -2101,7 +2956,7 @@
</property>
</bean></programlisting>
<para>The <code>fromContext</code> attribute can take the following values from the
- <code>org.jboss.beans.metadata.api.model.FromContext</code> enum:</para>
+ <code>org.jboss.beans.metadata.api.model.FromContext</code> enum:</para>
<itemizedlist>
<listitem>
<para>name - the name of the bean</para>
@@ -2110,7 +2965,8 @@
<para>aliases - a list of the bean's other names</para>
</listitem>
<listitem>
- <para>metadata - information about the bean from annotations or deployment descriptors</para>
+ <para>metadata - information about the bean from annotations or deployment
+ descriptors</para>
</listitem>
<listitem>
<para>beaninfo - information about the bean from the bean class</para>
@@ -2126,13 +2982,24 @@
</listitem>
</itemizedlist>
<important>
- <para>All context information is wrapped into unmodifiable objects to prevent the user from changing anything outside of the microcontainer's control. </para>
+ <para>All context information is wrapped into unmodifiable objects to prevent the user
+ from changing anything outside of the microcontainer's control. </para>
</important>
</section>
<section>
<title>Auto-wiring</title>
- <para>Having to write large amounts of configuration information just to wire together sometimes trivial beans can often be frustrating. For this reason JBoss Microcontainer provides the ability to autowire beans. Auto-wiring means that a bean's properties or constructor/factory-method parameters are set by automatically searching for matching beans. The benefit is that you don't have to specify the names of the beans to inject yourself but the drawback is that this only works reliably for small environments. If you have an environment where more than one bean matches the property or parameter being set then it becomes unclear from the configuration information which one will be chosen during deployment. </para>
- <para>To use auto-wiring you simply need to specify an @Inject annotation or <inject> element without a <code>bean</code> attribute for the parameter or property that you wish to set.</para>
+ <para>Having to write large amounts of configuration information just to wire together
+ sometimes trivial beans can often be frustrating. For this reason JBoss Microcontainer
+ provides the ability to autowire beans. Auto-wiring means that a bean's properties or
+ constructor/factory-method parameters are set by automatically searching for matching
+ beans. The benefit is that you don't have to specify the names of the beans to inject
+ yourself but the drawback is that this only works reliably for small environments. If you
+ have an environment where more than one bean matches the property or parameter being set
+ then it becomes unclear from the configuration information which one will be chosen during
+ deployment. </para>
+ <para>To use auto-wiring you simply need to specify an @Inject annotation or <inject>
+ element without a <code>bean</code> attribute for the parameter or property that you wish
+ to set.</para>
<programlisting role="JAVA">@Inject
public class Example(ThreadPool pool) {
...
@@ -2149,7 +3016,10 @@
<programlisting role="XML"><bean name="propInj" class="com.acme.Example">
<property name="threadPool"><inject/></property>
</bean> </programlisting>
- <para>By default the microcontainer will search for a bean whose class matches that of the parameter/property. However you can change this behaviour when auto-wiring properties so that the microcontainer searches for a bean whose name matches the property name. This is done using the <code>type</code> attribute of the <inject> element.</para>
+ <para>By default the microcontainer will search for a bean whose class matches that of the
+ parameter/property. However you can change this behaviour when auto-wiring properties so
+ that the microcontainer searches for a bean whose name matches the property name. This is
+ done using the <code>type</code> attribute of the <inject> element.</para>
<programlisting role="JAVA">@Inject(type=AutowireType.By_NAME)
public void setThreadPool(ThreadPool pool) {
...
@@ -2157,8 +3027,14 @@
<programlisting role="XML"><bean name="propInj" class="com.acme.Example">
<property name="threadPool"><inject type="ByName"/></property>
</bean> </programlisting>
- <para>The <code>org.jboss.beans.metadata.api.model.AutowireType</code> enum defines the valid values to use. <code>AutowireType.By_CLASS</code> is the default if using annotations, and <code>ByClass</code> the default when using xml.</para>
- <para>If no match is found then by default the deployment will fail but for certain properties it may be acceptable for injection to occur after deployment. In these cases you can specify that a property should be set using a callback mechanism whenever a suitable bean becomes available. This is done using the <code>option</code> attribute.</para>
+ <para>The <code>org.jboss.beans.metadata.api.model.AutowireType</code> enum defines the
+ valid values to use. <code>AutowireType.By_CLASS</code> is the default if using
+ annotations, and <code>ByClass</code> the default when using xml.</para>
+ <para>If no match is found then by default the deployment will fail but for certain
+ properties it may be acceptable for injection to occur after deployment. In these cases
+ you can specify that a property should be set using a callback mechanism whenever a
+ suitable bean becomes available. This is done using the <code>option</code>
+ attribute.</para>
<programlisting role="JAVA">@Inject(type=AutowireType.By_NAME, option=InjectOption.CALLBACK)
public void setThreadPool(ThreadPool pool) {
...
@@ -2166,8 +3042,16 @@
<programlisting role="XML"><bean name="propInj" class="com.acme.Example">
<property name="threadPool"><inject type="ByName" option="Callback"/></property>
</bean> </programlisting>
- <para>The <code>org.jboss.beans.metadata.api.model.InjectOption</code> enum defines the valid values to use. <code>InjectOption.STRICT</code> is the default when using annotations, and <code>Strict</code> the default when using xml. Another possible value for <code>option</code> is <code>InjectOption.OPTIONAL</code> when using annotations, and <code>Optional</code> when using xml, if this option is used the dependency is injected if present at injection time, otherwise it is ignored.</para>
- <para>Sometimes you may want to prevent a bean from being injected automatically using auto-wiring. In these situations you can set its <code>autowire-candidate</code> attribute to false so that it's not included in the search for matching beans. This is not currently possible using annotations as there is no annotation to declare a bean.</para>
+ <para>The <code>org.jboss.beans.metadata.api.model.InjectOption</code> enum defines the
+ valid values to use. <code>InjectOption.STRICT</code> is the default when using
+ annotations, and <code>Strict</code> the default when using xml. Another possible value
+ for <code>option</code> is <code>InjectOption.OPTIONAL</code> when using annotations, and
+ <code>Optional</code> when using xml, if this option is used the dependency is injected
+ if present at injection time, otherwise it is ignored.</para>
+ <para>Sometimes you may want to prevent a bean from being injected automatically using
+ auto-wiring. In these situations you can set its <code>autowire-candidate</code> attribute
+ to false so that it's not included in the search for matching beans. This is not
+ currently possible using annotations as there is no annotation to declare a bean.</para>
<programlisting role="XML"><bean name="ignored" class="com.acme.Example" autowire-candidate="false" /></programlisting>
</section>
</chapter>
@@ -2175,7 +3059,10 @@
<title>Defining dependencies</title>
<section>
<title>POJO dependencies</title>
- <para>Dependencies between beans (POJO instances) are created when they are wired together through the use of the @Inject annotation or <inject> element. Specifically a bean which has another bean injected into it is said to be dependent on the other bean. For example here we have a Person bean that is dependent on an Address bean.</para>
+ <para>Dependencies between beans (POJO instances) are created when they are wired together
+ through the use of the @Inject annotation or <inject> element. Specifically a bean
+ which has another bean injected into it is said to be dependent on the other bean. For
+ example here we have a Person bean that is dependent on an Address bean.</para>
<programlisting role="JAVA">public class Person {
@Inject(bean="AddressBean")
public void setAddress(Address address) {
@@ -2187,35 +3074,56 @@
<inject bean="AddressBean"/>
</property>
</bean></programlisting>
- <para>The reason for the dependency is because the address property of the Person bean cannot be configured until the Address bean has been deployed. Internally JBoss Microcontainer enforces this by moving each bean through a series of states during deployment. Consequently the Person bean will be prevented from moving to the CONFIGURED state until the Address bean has reached the INSTALLED state representing a deployed bean.</para>
- <para>By default there are 8 states that a bean can be in during deployment and undeployment:</para>
+ <para>The reason for the dependency is because the address property of the Person bean
+ cannot be configured until the Address bean has been deployed. Internally JBoss
+ Microcontainer enforces this by moving each bean through a series of states during
+ deployment. Consequently the Person bean will be prevented from moving to the CONFIGURED
+ state until the Address bean has reached the INSTALLED state representing a deployed
+ bean.</para>
+ <para>By default there are 8 states that a bean can be in during deployment and
+ undeployment:</para>
<itemizedlist>
<listitem>
- <para>NOT_INSTALLED - Metadata describing the bean has been parsed from annotations or a deployment descriptor.</para>
+ <para>NOT_INSTALLED - Metadata describing the bean has been parsed from annotations or a
+ deployment descriptor.</para>
</listitem>
<listitem>
- <para>PRE_INSTALL - the scope of the bean within the microcontainer has been discovered and classloading dependencies have been resolved.</para>
+ <para>PRE_INSTALL - the scope of the bean within the microcontainer has been discovered
+ and classloading dependencies have been resolved.</para>
</listitem>
<listitem>
<para>DESCRIBED - Any AOP dependencies have been added.</para>
</listitem>
<listitem>
- <para>INSTANTIATED -All dependencies have been resolved to construct the bean. These include; a classloader exists, the class can be found, a constructor or factory method can be found and any parameter injections can be resolved.</para>
+ <para>INSTANTIATED -All dependencies have been resolved to construct the bean. These
+ include; a classloader exists, the class can be found, a constructor or factory method
+ can be found and any parameter injections can be resolved.</para>
</listitem>
<listitem>
- <para>CONFIGURED - All the property injections can be resolved. This includes any injections within collections.</para>
+ <para>CONFIGURED - All the property injections can be resolved. This includes any
+ injections within collections.</para>
</listitem>
<listitem>
- <para>CREATE - All the beans metioned in the @Depends annotation or <depends> element have reached the CREATE state including any parameter injections in the create method.</para>
+ <para>CREATE - All the beans metioned in the @Depends annotation or <depends>
+ element have reached the CREATE state including any parameter injections in the create
+ method.</para>
</listitem>
<listitem>
- <para>START - All the beans metioned in the @Depends annotation or <depends> element have reached the START state including any parameter injections in the start method.</para>
+ <para>START - All the beans metioned in the @Depends annotation or <depends>
+ element have reached the START state including any parameter injections in the start
+ method.</para>
</listitem>
<listitem>
- <para>INSTALLED - Any deployment actions have been processed and the bean has been added to the list of items that the controller supplies.</para>
+ <para>INSTALLED - Any deployment actions have been processed and the bean has been added
+ to the list of items that the controller supplies.</para>
</listitem>
</itemizedlist>
- <para>You may notice that we refer to injections being resolved in the above list. This is a shorthand way of saying that an injected bean has reached a certain state. In our simple example we can say that the injected Address bean has been resolved when it reaches the INSTALLED state. If necessary you can change the state that an injected bean must reach before being considered resolved by using the <code>dependentState</code>/<code>state</code> attribute.</para>
+ <para>You may notice that we refer to injections being resolved in the above list. This is a
+ shorthand way of saying that an injected bean has reached a certain state. In our simple
+ example we can say that the injected Address bean has been resolved when it reaches the
+ INSTALLED state. If necessary you can change the state that an injected bean must reach
+ before being considered resolved by using the
+ <code>dependentState</code>/<code>state</code> attribute.</para>
<programlisting role="JAVA">public class Person {
@Inject(bean="OtherBean",
dependentState="Instantiated")
@@ -2228,9 +3136,15 @@
<inject bean="Address" state="Instantiated"/>
</property>
</bean></programlisting>
- <para>Here we are saying the Address bean needs to reach the INSTANTIATED state before the Person bean can use it to configure the address property and thus move to the CONFIGURED state. Be aware that setting the state value to <code>Described</code> in this case would not be appropriate since we must first have an instance of the Address bean before we can inject a reference to it into another bean.</para>
+ <para>Here we are saying the Address bean needs to reach the INSTANTIATED state before the
+ Person bean can use it to configure the address property and thus move to the CONFIGURED
+ state. Be aware that setting the state value to <code>Described</code> in this case would
+ not be appropriate since we must first have an instance of the Address bean before we can
+ inject a reference to it into another bean.</para>
<note>
- <para>The following six values can be used for the <code>dependentState</code>/<code>state</code> attribute to control injection. If no value is specified then by default the Installed state is used.</para>
+ <para>The following six values can be used for the
+ <code>dependentState</code>/<code>state</code> attribute to control injection. If no
+ value is specified then by default the Installed state is used.</para>
<itemizedlist>
<listitem>
<para>Described</para>
@@ -2252,7 +3166,9 @@
</listitem>
</itemizedlist>
</note>
- <para>The ability to control when injection occurs is especially useful when dealing with circular dependencies. A circular dependency happens when one bean depends on another which in turn depends on the first bean:</para>
+ <para>The ability to control when injection occurs is especially useful when dealing with
+ circular dependencies. A circular dependency happens when one bean depends on another
+ which in turn depends on the first bean:</para>
<programlisting role="XML"><bean name="Circular1" class="org.jboss.example.InjectionBean">
<constructor>
<parameter>Circular1</parameter>
@@ -2272,7 +3188,10 @@
<inject bean="Circular1"/>
</property>
</bean> </programlisting>
- <para>In this configuration the microcontainer is unable to deploy either bean since each one is waiting in the INSTANTIATED state waiting for the other to reach the INSTALLED state. To solve this we can use the <code>state</code> attribute to specify that Circular1 can be injected into Circular2 as soon as it reaches the INSTANTIATED state. </para>
+ <para>In this configuration the microcontainer is unable to deploy either bean since each
+ one is waiting in the INSTANTIATED state waiting for the other to reach the INSTALLED
+ state. To solve this we can use the <code>state</code> attribute to specify that Circular1
+ can be injected into Circular2 as soon as it reaches the INSTANTIATED state. </para>
<programlisting role="XML"><bean name="Circular1" class="org.jboss.example.InjectionBean">
<constructor>
<parameter>Circular1</parameter>
@@ -2292,11 +3211,16 @@
<inject bean="Circular1" state="Instantiated"/>
</property>
</bean></programlisting>
- <para>Circular2 can thus progress to the INSTALLED state whereupon Circular1 will inject it into its <code>other</code> property and continue deploying.</para>
+ <para>Circular2 can thus progress to the INSTALLED state whereupon Circular1 will inject it
+ into its <code>other</code> property and continue deploying.</para>
</section>
<section>
<title>Supply and Demand</title>
- <para>Sometimes dependencies may exist between beans that are not wired together using injections. One example would be a bean representing a service that requires a transaction manager to be present in JNDI before it can start. In these situations we can use a @Demand annotation or <demand> element to specify that we need another bean to be deployed before we can reach a certain state.</para>
+ <para>Sometimes dependencies may exist between beans that are not wired together using
+ injections. One example would be a bean representing a service that requires a transaction
+ manager to be present in JNDI before it can start. In these situations we can use a
+ @Demand annotation or <demand> element to specify that we need another bean to be
+ deployed before we can reach a certain state.</para>
<programlisting role="JAVA">@Demands(@Demand("TransactionManager"))
public class Example {
...
@@ -2306,7 +3230,10 @@
<bean name="Name2" class="com.acme.Example">
<demand>TransactionManager</demand>
</bean></programlisting>
- <para>By default the bean declaring the demand will wait until the beans it requires have been deployed before it moves to the INSTANTIATED state. However you can change this using the <code>whenRequired</code>/<code>state</code> attribute to specify a different state that can only be reached once all the beans listed as demands have been deployed.</para>
+ <para>By default the bean declaring the demand will wait until the beans it requires have
+ been deployed before it moves to the INSTANTIATED state. However you can change this using
+ the <code>whenRequired</code>/<code>state</code> attribute to specify a different state
+ that can only be reached once all the beans listed as demands have been deployed.</para>
<programlisting role="JAVA">@Demands(@Demand("TransactionManager", whenRequired="Start"))
public class Example {
...
@@ -2316,7 +3243,9 @@
<bean name="Name2" class="com.acme.Example">
<demand state="Start">TransactionManager</demand>
</bean></programlisting>
- <para>Here we are stating that the TransactionManager bean must be deployed before the Name2 bean can reach the Start state. To specify multiple dependencies on other beans you simply need to list @Demand annotations or nest <demand> elements:</para>
+ <para>Here we are stating that the TransactionManager bean must be deployed before the Name2
+ bean can reach the Start state. To specify multiple dependencies on other beans you simply
+ need to list @Demand annotations or nest <demand> elements:</para>
<programlisting role="JAVA">@Demands(@Demand("TransactionManager", whenRequired="Start"),
@Demand("OtherBean"))
public class Example {
@@ -2330,7 +3259,11 @@
<demand state="Start">TransactionManager</demand>
<demand>OtherBean</demand>
</bean></programlisting>
- <para>In addition to specifying dependencies on other beans you can also specify dependencies on arbitrary objects that other beans supply. This can be useful if a bean depends on something that is provided by multiple beans but doesn't necessarily care which one provides it. To create these arbitrary objects you need to use the @Supply annotation or <supply> element.</para>
+ <para>In addition to specifying dependencies on other beans you can also specify
+ dependencies on arbitrary objects that other beans supply. This can be useful if a bean
+ depends on something that is provided by multiple beans but doesn't necessarily care
+ which one provides it. To create these arbitrary objects you need to use the @Supply
+ annotation or <supply> element.</para>
<programlisting role="JAVA">@Supply("SomethingUseful")
public class Provider {
...
@@ -2375,12 +3308,182 @@
<bean name="OtherConsumer" class="com.acme.Consumer">
<demand>SomethingElse</demand>
</bean> </programlisting>
- <para>Behind the scenes JBoss Microcontainer creates a bean of type java.lang.Object for each @Supply annotation or <supply> element named after its value. These beans are then moved automatically through all of the states to reach the INSTALLED state and become deployed. From this point onwards the @Demand annotation and <demand> element work in exactly the same way as before, checking to see if the named bean has been deployed before allowing the bean declaring the demands to move to a certain state.</para>
+ <para>Behind the scenes JBoss Microcontainer creates a bean of type java.lang.Object for
+ each @Supply annotation or <supply> element named after its value. These beans are
+ then moved automatically through all of the states to reach the INSTALLED state and become
+ deployed. From this point onwards the @Demand annotation and <demand> element work
+ in exactly the same way as before, checking to see if the named bean has been deployed
+ before allowing the bean declaring the demands to move to a certain state.</para>
+ <section>
+ <title>Customizing how Supply and Demand are Matched</title>
+ <para>To control how a demand matches a supply you can specify a transformer that
+ customizes how the demand value is matched to a supply value. The matching is customized
+ by specifying a transformer to a demand. The transformer is an implementation of the
+ following org.jboss.kernel.api.dependency.MatcherTransformer and associated
+ org.jboss.kernel.api.dependency.Matcher interfaces: </para>
+ <programlisting role="Java">
+package org.jboss.kernel.api.dependency;
+
+public interface MatcherTransformer
+{
+ /**
+ * Transform value param to Matcher instance.
+ *
+ * @param value the value
+ * @return Matcher instance
+ */
+ Matcher transform(Object value);
+}
+
+package org.jboss.kernel.api.dependency;
+
+/**
+ * Matcher.
+ * Match this instance against any object.
+ *
+ */
+public interface Matcher
+{
+ /**
+ * Do we match this object other param.
+ *
+ * @param other the object to match
+ * @return true if we match the other param, false otherwise
+ */
+ boolean match(Object other);
+
+ /**
+ * Do we need an exact match.
+ * No match or single match is acceptable.
+ *
+ * @return true if matcher accepts only exact matches
+ */
+ boolean needExactMatch();
+}
+
+ </programlisting>
+ <para>There are a number of pre-existing implementations that may be referenced by a one
+ of the following keys:<simplelist>
+ <member>default : org.jboss.kernel.plugins.dependency.DefaultMatcherFactory a
+ transformer that builds a matcher which simply uses object equality between the
+ demand and supply values.</member>
+ <member>interval : org.jboss.kernel.plugins.dependency.IntervalMatcherTransformer a
+ transformer that builds a matcher by using the demand value as a mathematical range
+ to test supply values as integers against.</member>
+ <member>regex : org.jboss.kernel.plugins.dependency.RegexpMatcherTransformer a
+ transformer that builds a matcher by using the demand value as a regular expression
+ to match against the supply values.</member>
+ </simplelist></para>
+ <para>Any other transformer value is treated as a class name which implements the
+ MatcherTransformer interface. Below is an example of a demand that specifies a custom
+ implementation of org.jboss.kernel.api.dependency.MatcherTransformer.</para>
+ <programlisting role="Java">import org.jboss.beans.metadata.api.annotations.Demands;
+import org.jboss.beans.metadata.api.annotations.Demand;
+
+ at Demands({@Demand(value = "fragment", transformer = "org.jboss.test.kernel.dependency.support.CapabilityMatcherTransfomer")})
+public class CustomDemander
+{
+}
+ </programlisting>
+ <para>The corresponding CapabilityMatcherTransfomer implementation is given in the
+ following listing. This transformer treats </para>
+ <programlisting role="Java">
+package org.jboss.test.kernel.dependency.support;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.jboss.kernel.api.dependency.Matcher;
+import org.jboss.kernel.api.dependency.NonNullMatcherTransformer;
+import org.jboss.kernel.api.dependency.ClassMatcher;
+
+public class CapabilityMatcherTransfomer extends NonNullMatcherTransformer
+{
+ protected Matcher internalTransform(Object value)
+ {
+ return new PropertiesMatcher(value.toString());
+ }
+
+ private class PropertiesMatcher extends ClassMatcher<Properties>
+ {
+ private Properties props;
+
+ public PropertiesMatcher(String string)
+ {
+ super(Properties.class);
+ if (string == null)
+ throw new IllegalArgumentException("Null fragment");
+
+ this.props = new Properties();
+ ByteArrayInputStream bais = new ByteArrayInputStream(string.getBytes());
+ try
+ {
+ props.load(bais);
+ }
+ catch(IOException e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ protected boolean matchByType(Properties other)
+ {
+ boolean matches = false;
+ for(Object key : props.keySet())
+ {
+ String skey = key.toString();
+ String requires = props.getProperty(skey);
+ String capability = other.getProperty(skey);
+ if(requires.equalsIgnoreCase(capability))
+ matches |= true;
+ }
+ return matches;
+ }
+
+ public String toString()
+ {
+ return props.toString();
+ }
+ }
+}
+ </programlisting>
+ <para>An example of bean deployments where a demand matches a supply based on this matcher
+ is shown in the following
+ listing:<programlisting role="XML"><?xml version="1.0" encoding="UTF-8"?>
+
+<deployment xmlns="urn:jboss:bean-deployer:2.0">
+ <bean name="capabilitySupplier" class="org.jboss.test.kernel.dependency.support.SimpleBeanImpl">
+ <supply class="java.util.Properties">
+ key1=value1
+ key2=value2
+ </supply>
+ <supply class="java.util.Properties">
+ key2.1=value2.1
+ key2.2=value2.2
+ </supply>
+ </bean>
+
+ <bean name="requiresDemand" class="org.jboss.test.kernel.dependency.support.SimpleBeanImpl">
+ <demand transformer="org.jboss.test.kernel.dependency.support.CapabilityMatcherTransfomer">
+ key1=value1
+ key2=value2
+ </demand>
+ </bean>
+</deployment>
+</programlisting></para>
+ </section>
</section>
<section>
<title>Service dependencies</title>
- <para>So far we have considered how POJOs depend on each other either explictly through injections or implicitly through the use of arbitrary objects supplied during deployment. These dependencies help us to assemble POJOs in a well defined order to create services but what about when one service depends on another?</para>
- <para>Services typically consist of many individual POJOs wired together in order to perform a well-defined function. Once a service is deployed it can be stopped and started as many times as necessary in order to make best use of valuable resources such as CPU and memory. This is possible thanks to the service lifecycle:</para>
+ <para>So far we have considered how POJOs depend on each other either explictly through
+ injections or implicitly through the use of arbitrary objects supplied during deployment.
+ These dependencies help us to assemble POJOs in a well defined order to create services
+ but what about when one service depends on another?</para>
+ <para>Services typically consist of many individual POJOs wired together in order to perform
+ a well-defined function. Once a service is deployed it can be stopped and started as many
+ times as necessary in order to make best use of valuable resources such as CPU and memory.
+ This is possible thanks to the service lifecycle:</para>
<itemizedlist>
<listitem>
<para>create - the service has been deployed</para>
@@ -2395,7 +3498,11 @@
<para>destroy - the service is being undeployed</para>
</listitem>
</itemizedlist>
- <para>If one service depends on another then we need someway to ensure that they are created and started in the correct order. Similarly we also need to ensure that they are stopped and destroyed correctly. This is done by adding a @Depends annotation or <depends> element to the POJO that defines a service's public methods and specifying the name of the bean that represents the other service.</para>
+ <para>If one service depends on another then we need someway to ensure that they are created
+ and started in the correct order. Similarly we also need to ensure that they are stopped
+ and destroyed correctly. This is done by adding a @Depends annotation or <depends>
+ element to the POJO that defines a service's public methods and specifying the name
+ of the bean that represents the other service.</para>
<programlisting role="JAVA">@Depends("AnotherBean")
public class MyBean {
...
@@ -2405,7 +3512,12 @@
<bean name="MyBean" class="org.jboss.test.Example">
<depends>AnotherBean</depends>
</bean></programlisting>
- <para>The microcontainer will then ensure that the beans, representing the two services, are deployed in the correct order. Specifically the beans named in the @Depends or <depends> values will reach their CREATE states before the bean declaring the dependencies reaches its CREATE state. The same is true for the START, STOP and DESTROY states. It's possible to define multiple dependencies on other services simply by listing names or nesting <depends> elements:</para>
+ <para>The microcontainer will then ensure that the beans, representing the two services, are
+ deployed in the correct order. Specifically the beans named in the @Depends or
+ <depends> values will reach their CREATE states before the bean declaring the
+ dependencies reaches its CREATE state. The same is true for the START, STOP and DESTROY
+ states. It's possible to define multiple dependencies on other services simply by
+ listing names or nesting <depends> elements:</para>
<programlisting role="JAVA">@Depends("AnotherBean", "YetAnotherBean")
public class MyBean {
...
@@ -2422,8 +3534,16 @@
<title>Adding deployment behaviour</title>
<section>
<title>Deployment actions</title>
- <para>In Part I - Getting Started we covered how to add behaviour to beans during the deployment and undeployment process using AOP lifecycle callbacks. This gave us a powerful way to apply common logic to a number of beans, identified using pointcut expressions, at various points in their lifecycles. However setting up AOP lifecycle callbacks for occasions when an individual bean needs to call arbitrary methods before deployment or after undeployment can be time consuming. Fortunately JBoss Microcontainer provides an alternative way to do this using deployment/undeployment actions.</para>
- <para>To specify a method within a bean that should be called after the bean reaches the START state you should use the @InstallMethod annotation or <install> element as follows:</para>
+ <para>In Part I - Getting Started we covered how to add behaviour to beans during the
+ deployment and undeployment process using AOP lifecycle callbacks. This gave us a powerful
+ way to apply common logic to a number of beans, identified using pointcut expressions, at
+ various points in their lifecycles. However setting up AOP lifecycle callbacks for
+ occasions when an individual bean needs to call arbitrary methods before deployment or
+ after undeployment can be time consuming. Fortunately JBoss Microcontainer provides an
+ alternative way to do this using deployment/undeployment actions.</para>
+ <para>To specify a method within a bean that should be called after the bean reaches the
+ START state you should use the @InstallMethod annotation or <install> element as
+ follows:</para>
<programlisting role="JAVA">@InstallMethod
public String doSomething() {
...
@@ -2431,7 +3551,8 @@
<programlisting role="XML"><bean name="SimpleBean" class="org.jboss.example.SimpleBean">
<install method="doSomething"/>
</bean></programlisting>
- <para>Parameters can also be passed in using nested <parameter> elements or by defining values with annotations.</para>
+ <para>Parameters can also be passed in using nested <parameter> elements or by
+ defining values with annotations.</para>
<programlisting role="JAVA">@InstallMethod
public String doSomething(@StringValue("10") Integer integer, @StringValue("my string") String string) {
...
@@ -2442,7 +3563,8 @@
<parameter>my string</parameter>
</install>
</bean></programlisting>
- <para>If necessary multiple install methods can be defined. They will be called in the order in which they appear as annotations or in the deployment descriptor.</para>
+ <para>If necessary multiple install methods can be defined. They will be called in the order
+ in which they appear as annotations or in the deployment descriptor.</para>
<programlisting role="JAVA">@InstallMethod
public String doSomething() {
...
@@ -2456,7 +3578,8 @@
<install method="doSomething"/>
<install method="doSomethingElse"/>
</bean></programlisting>
- <para>During undeployment uninstall methods can also be defined which will be called before the bean reaches the STOP state.</para>
+ <para>During undeployment uninstall methods can also be defined which will be called before
+ the bean reaches the STOP state.</para>
<programlisting role="JAVA">@UninstallMethod
public String doSomething() {
...
@@ -2470,7 +3593,10 @@
<uninstall method="doSomething"/>
<uninstall method="doSomethingElse"/>
</bean></programlisting>
- <para>If you want a bean to call a method on another bean during deployment or undeployment then you can use the @ExternalInstalls/@ExternalUninstalls annotations or the <code>bean</code> attribute of the <install>/<uninstall> elements. You can also define parameters to pass in if necessary.</para>
+ <para>If you want a bean to call a method on another bean during deployment or undeployment
+ then you can use the @ExternalInstalls/@ExternalUninstalls annotations or the
+ <code>bean</code> attribute of the <install>/<uninstall> elements. You can
+ also define parameters to pass in if necessary.</para>
<programlisting role="JAVA">@ExternalInstalls(@ExternalInstall(bean="OtherBean", method="doWork")
@ExternalUninstalls(@ExternalInstall(bean="OtherBean", method="doBye", parameters="{@Value(string=@StringValue("Goodbye")), at Value(string=@StringValue("Saturday"))}"))
public class MyBean {
@@ -2484,9 +3610,12 @@
</uninstall>
</bean></programlisting>
<note>
- <para>Currently both the @ExternalInstalls and @ExternalUninstalls annotations take a list of @ExternalInstall annotations. </para>
+ <para>Currently both the @ExternalInstalls and @ExternalUninstalls annotations take a list
+ of @ExternalInstall annotations. </para>
</note>
- <para>By default the other bean must have reached the INSTALLED state before its method will be called. This can be changed by using the <code>dependentState</code>/<code>state</code> attribute.</para>
+ <para>By default the other bean must have reached the INSTALLED state before its method will
+ be called. This can be changed by using the <code>dependentState</code>/<code>state</code>
+ attribute.</para>
<programlisting role="JAVA">@ExternalInstalls(@ExternalInstall(bean="OtherBean", method="doWork", dependentState="Configured")
public class MyBean {
...
@@ -2497,9 +3626,16 @@
</section>
<section>
<title>Deployment callbacks</title>
- <para>Sometimes it is useful for a bean to know when other beans have been deployed. For example a manager bean might want to keep a list of all of the beans it manages. Rather than placing code into each managed bean to register itself with the manager when deployed, it would be better if the manager was automatically called back each time to notify it. JBoss Microcontainer allows such callbacks to occur during deployment and undeployment using the @Install/@Uninstall annotations and <incallback>/<uncallback> elements.</para>
+ <para>Sometimes it is useful for a bean to know when other beans have been deployed. For
+ example a manager bean might want to keep a list of all of the beans it manages. Rather
+ than placing code into each managed bean to register itself with the manager when
+ deployed, it would be better if the manager was automatically called back each time to
+ notify it. JBoss Microcontainer allows such callbacks to occur during deployment and
+ undeployment using the @Install/@Uninstall annotations and
+ <incallback>/<uncallback> elements.</para>
<note>
- <para><incallback> is an abbreviation of installCallback and <uncallback> is an abbreviation of uninstallCallback.</para>
+ <para><incallback> is an abbreviation of installCallback and <uncallback> is
+ an abbreviation of uninstallCallback.</para>
</note>
<programlisting role="JAVA">public class Example {
@Install
@@ -2516,7 +3652,14 @@
<incallback method="addEditor"/>
<uncallback method="removeEditor"/>
</bean> </programlisting>
- <para>In the above example the addEditor method of the editorHolder bean will be called each time a bean of class Editor is deployed. Similarly the removeEditor method will be called each time a bean of class Editor is undeployed. By default the bean class to look for is determined by the class of the parameter in the callback method. If there are multiple methods with the same name but different parameter types then you can add a <code>signature</code> attribute to the <incallback>/<uncallback> element to specify the correct one. This is not required when using annotations as we simply annotate the method we want.</para>
+ <para>In the above example the addEditor method of the editorHolder bean will be called each
+ time a bean of class Editor is deployed. Similarly the removeEditor method will be called
+ each time a bean of class Editor is undeployed. By default the bean class to look for is
+ determined by the class of the parameter in the callback method. If there are multiple
+ methods with the same name but different parameter types then you can add a
+ <code>signature</code> attribute to the <incallback>/<uncallback> element to
+ specify the correct one. This is not required when using annotations as we simply annotate
+ the method we want.</para>
<programlisting role="JAVA">public class Example {
public void addEditor(Editor editor) {
...
@@ -2530,7 +3673,8 @@
<programlisting role="XML"><bean name="editorHolder" class="com.acme.Example">
<incallback method="addEditor" signature="org.jboss.example.DifferentEditor"/>
</bean> </programlisting>
- <para>Multiple callback methods can be defined per bean. They will be called in the order that they appear as annotations or in the deployment descriptor.</para>
+ <para>Multiple callback methods can be defined per bean. They will be called in the order
+ that they appear as annotations or in the deployment descriptor.</para>
<programlisting role="JAVA">public class Example {
@Install
public void addEditor(Editor editor) {
@@ -2558,7 +3702,9 @@
<uncallback method="removeEditor"/>
<uncallback method="removeViewer"/>
</bean> </programlisting>
- <para>By default each callback will be executed when beans matching the parameter types reach the INSTALLED state. This can be changed if necessary using the <code>dependentState</code>/<code>state</code> attribute.</para>
+ <para>By default each callback will be executed when beans matching the parameter types
+ reach the INSTALLED state. This can be changed if necessary using the
+ <code>dependentState</code>/<code>state</code> attribute.</para>
<programlisting role="JAVA">public class Example {
@Install(dependentState="Configured")
public void addEditor(Editor editor) {
@@ -2568,8 +3714,10 @@
<programlisting role="XML"><bean name="editorHolder" class="com.acme.Example">
<incallback method="addEditor" state="Configured"/>
</bean> </programlisting>
- <para>Here we are declaring that the addEditor method of the editorHolder bean should be called when any beans of class Editor reach the CONFIGURED state.</para>
- <para>It is also possible to configure when the callback methods are executed during the deployment of the bean using the <code>whenRequired</code> attribute.</para>
+ <para>Here we are declaring that the addEditor method of the editorHolder bean should be
+ called when any beans of class Editor reach the CONFIGURED state.</para>
+ <para>It is also possible to configure when the callback methods are executed during the
+ deployment of the bean using the <code>whenRequired</code> attribute.</para>
<programlisting role="JAVA">public class Example {
@Install(whenRequired="Installed")
public void addEditor(Editor editor) {
@@ -2579,8 +3727,12 @@
<programlisting role="XML"><bean name="editorHolder" class="com.acme.Example">
<incallback method="addEditor" whenRequired="Installed"/>
</bean> </programlisting>
- <para>Here we are declaring that the addEditor method will be called before the editorHolder bean reaches the INSTALLED state. By default the callbacks are exceuted before the bean reaches the CONFIGURED state.</para>
- <para>Finally we can also control when the callback methods are executed depending on how many beans matching the parameter class have been deployed. This is done using the <code>cardinality</code> attribute.</para>
+ <para>Here we are declaring that the addEditor method will be called before the editorHolder
+ bean reaches the INSTALLED state. By default the callbacks are exceuted before the bean
+ reaches the CONFIGURED state.</para>
+ <para>Finally we can also control when the callback methods are executed depending on how
+ many beans matching the parameter class have been deployed. This is done using the
+ <code>cardinality</code> attribute.</para>
<programlisting role="JAVA">public class Example {
@Install(cardinality="2..n")
public void addEditor(Editor editor) {
@@ -2590,15 +3742,30 @@
<programlisting role="XML"><bean name="editorHolder" class="com.acme.Example">
<incallback method="addEditor" cardinality="2..n"/>
</bean> </programlisting>
- <para>Here we are declaring that the addEditor method of the editorHolder bean will only be called when two or more beans of class Editor have been deployed.</para>
+ <para>Here we are declaring that the addEditor method of the editorHolder bean will only be
+ called when two or more beans of class Editor have been deployed.</para>
<note>
- <para>When using callbacks with collection classes as parameters only the following basic interfaces are currently supported; List, Set and Queue. This is done using the <code>BasicCollectionCallbackItemFactory</code> implementation. You can change this if required by setting the <code>org.jboss.dependency.collectionCallbackItemFactory</code> system property to the fully-qualified class name of your <code>CollectionCallbackItemFactory</code> implementation.</para>
+ <para>When using callbacks with collection classes as parameters only the following basic
+ interfaces are currently supported; List, Set and Queue. This is done using the
+ <code>BasicCollectionCallbackItemFactory</code> implementation. You can change this if
+ required by setting the <code>org.jboss.dependency.collectionCallbackItemFactory</code>
+ system property to the fully-qualified class name of your
+ <code>CollectionCallbackItemFactory</code> implementation.</para>
</note>
</section>
<section>
<title>Service lifecycle</title>
- <para>So far we have discussed how to define deployment/undeployment actions for beans together with callback methods for notification when other beans have been deployed/undeployed. These features are useful for adding behaviours to POJOs but what about adding behaviour to services?</para>
- <para>As explained in the previous chapter services have a lifecycle consisting of 4 stages; create, start, stop and destroy. The reason for this is because we want the ability to start and stop a deployed service repeatably at runtime to make best use of valuable resources such as CPU and memory. At each point in this lifecycle the microcontainer will look for a method to call in order to perform appropriate actions. For example starting a service may require that an object is bound into JNDI. By default these methods are named after the lifecycle stage that they represent.</para>
+ <para>So far we have discussed how to define deployment/undeployment actions for beans
+ together with callback methods for notification when other beans have been
+ deployed/undeployed. These features are useful for adding behaviours to POJOs but what
+ about adding behaviour to services?</para>
+ <para>As explained in the previous chapter services have a lifecycle consisting of 4 stages;
+ create, start, stop and destroy. The reason for this is because we want the ability to
+ start and stop a deployed service repeatably at runtime to make best use of valuable
+ resources such as CPU and memory. At each point in this lifecycle the microcontainer will
+ look for a method to call in order to perform appropriate actions. For example starting a
+ service may require that an object is bound into JNDI. By default these methods are named
+ after the lifecycle stage that they represent.</para>
<programlisting role="JAVA">public class LifecycleBean {
public void create() {
...
@@ -2616,8 +3783,14 @@
...
}
} </programlisting>
- <para>If methods like these are defined in your bean then they will be called as the bean is deployed and undeployed. Specifically the create and start methods are called as the bean reaches the CREATE and START states during deployment. The stop and destroy methods are called when the bean passes through the START and CREATE states during undeployment. Only those methods that are defined will be called. For example if you omit the start method then the bean will move to the START state without anything happening.</para>
- <para>If you want a different method to be called for any of the stages then you can provide an annotation or XML element to specify which one to use.</para>
+ <para>If methods like these are defined in your bean then they will be called as the bean is
+ deployed and undeployed. Specifically the create and start methods are called as the bean
+ reaches the CREATE and START states during deployment. The stop and destroy methods are
+ called when the bean passes through the START and CREATE states during undeployment. Only
+ those methods that are defined will be called. For example if you omit the start method
+ then the bean will move to the START state without anything happening.</para>
+ <para>If you want a different method to be called for any of the stages then you can provide
+ an annotation or XML element to specify which one to use.</para>
<programlisting role="JAVA">public class Example {
@Create
public void initialize() {
@@ -2659,7 +3832,9 @@
<parameter>5</parameter>
</start>
</bean> </programlisting>
- <para>Sometimes you may want lifecycle methods to be ignored. This can be done using the <code>ignore</code> attribute which if set to true prevents the microcontainer from calling the method when the corresponding lifecycle stage is reached.</para>
+ <para>Sometimes you may want lifecycle methods to be ignored. This can be done using the
+ <code>ignore</code> attribute which if set to true prevents the microcontainer from
+ calling the method when the corresponding lifecycle stage is reached.</para>
<programlisting role="JAVA">public class Example {
@Stop(ignored="true")
public void stop() {
@@ -2694,8 +3869,16 @@
<title>The Deployment Framework</title>
<chapter>
<title>Introduction</title>
- <para>JBoss Microcontainer at its core allows POJOs to be deployed into a runtime environment. It does this using a state machine to ensure that any dependencies on classloaders or other POJOs are satisfied before progressing through the various stages of creation and configuration. By design the state machine and dependency mechanisms are generic which means that we can also use them to deploy other types of runtime components such as MBeans or OSGi bundles. In order to do this we need a generic deployment framework to create the relevant metadata, handle different kinds of deployable components, and deal with any nested deployments.</para>
- <para>To understand why we cannot achieve this using the BasicXMLDeployer let's take a closer look at what it actually does:</para>
+ <para>JBoss Microcontainer at its core allows POJOs to be deployed into a runtime environment.
+ It does this using a state machine to ensure that any dependencies on classloaders or other
+ POJOs are satisfied before progressing through the various stages of creation and
+ configuration. By design the state machine and dependency mechanisms are generic which means
+ that we can also use them to deploy other types of runtime components such as MBeans or OSGi
+ bundles. In order to do this we need a generic deployment framework to create the relevant
+ metadata, handle different kinds of deployable components, and deal with any nested
+ deployments.</para>
+ <para>To understand why we cannot achieve this using the BasicXMLDeployer let's take a
+ closer look at what it actually does:</para>
<programlisting role="JAVA">public class BasicXMLDeployer extends BasicKernelDeployer
{
...
@@ -2739,7 +3922,10 @@
...
}</programlisting>
- <para>Once we call the deploy() method with a URL to the XML deployment descriptor an unmarshaller is created to convert the XML into an object representation of type KernelDeployment. We then process the KernelDeployment using the deploy() method of the superclass BasicKernelDeployer: </para>
+ <para>Once we call the deploy() method with a URL to the XML deployment descriptor an
+ unmarshaller is created to convert the XML into an object representation of type
+ KernelDeployment. We then process the KernelDeployment using the deploy() method of the
+ superclass BasicKernelDeployer: </para>
<programlisting role="JAVA">public class BasicKernelDeployer extends AbstractKernelDeployer
{
...
@@ -2764,9 +3950,12 @@
}
}
}</programlisting>
- <para>As you can see the BasicKernelDeployer simply calls the deploy() method of its superclass AbstractKernelDeployer before adding the deployment to a list.</para>
+ <para>As you can see the BasicKernelDeployer simply calls the deploy() method of its
+ superclass AbstractKernelDeployer before adding the deployment to a list.</para>
<note>
- <para>The reason for having BasicKernelDeployer is that it contains a shutdown() method which undeploys all the deployments in reverse order. This allows the microcontainer to shutdown gracefully.</para>
+ <para>The reason for having BasicKernelDeployer is that it contains a shutdown() method
+ which undeploys all the deployments in reverse order. This allows the microcontainer to
+ shutdown gracefully.</para>
</note>
<programlisting role="JAVA">public class AbstractKernelDeployer
{
@@ -2824,12 +4013,29 @@
...
}</programlisting>
- <para>The deploy() method in AbstractKernelDeployer performs a check to see if the deployment has already been installed before calling deployBeans() to split it up into the individual BeanMetaData instances. These are then deployed into the runtime environment using the deployBean() method which creates a KernelControllerContext for each BeanMetaData and installs it into the controller. It also takes care of deciding which classloader to use based on information contained in the KernelDeployment object. For example if the bean doesn't declare a classloader of its own then the deployment classloader (if defined) is used instead.</para>
- <para>The type hierarchy of BasicXMLDeployer restricts it to processing bean deployments of type KernelDeployment where each bean is represented by an instance of BeanMetaData. If we want to deploy other runtime components such as MBeans or OSGi bundles then we need to deal with other types of metadata and find a generic way to represent deployments containing them. Furthermore we need to find a flexibile way to process these deployments since each would have its own set of actions that need to take place. For example MBeans need to be registered with an MBean server before they can be considered 'deployed'.</para>
- <para>It turns out that we can use the state machine to meet these requirements by creating a DeploymentControllerContext to represent a deployment in the same way that a KernelControllerContext represents a POJO. This allows us to associate a number of deployment actions to different stages of the deployment process:</para>
+ <para>The deploy() method in AbstractKernelDeployer performs a check to see if the deployment
+ has already been installed before calling deployBeans() to split it up into the individual
+ BeanMetaData instances. These are then deployed into the runtime environment using the
+ deployBean() method which creates a KernelControllerContext for each BeanMetaData and
+ installs it into the controller. It also takes care of deciding which classloader to use
+ based on information contained in the KernelDeployment object. For example if the bean
+ doesn't declare a classloader of its own then the deployment classloader (if defined)
+ is used instead.</para>
+ <para>The type hierarchy of BasicXMLDeployer restricts it to processing bean deployments of
+ type KernelDeployment where each bean is represented by an instance of BeanMetaData. If we
+ want to deploy other runtime components such as MBeans or OSGi bundles then we need to deal
+ with other types of metadata and find a generic way to represent deployments containing
+ them. Furthermore we need to find a flexibile way to process these deployments since each
+ would have its own set of actions that need to take place. For example MBeans need to be
+ registered with an MBean server before they can be considered 'deployed'.</para>
+ <para>It turns out that we can use the state machine to meet these requirements by creating a
+ DeploymentControllerContext to represent a deployment in the same way that a
+ KernelControllerContext represents a POJO. This allows us to associate a number of
+ deployment actions to different stages of the deployment process:</para>
<itemizedlist>
<listitem>
- <para>PARSE - convert one or more deployment descriptors into object representations containing metadata</para>
+ <para>PARSE - convert one or more deployment descriptors into object representations
+ containing metadata</para>
</listitem>
<listitem>
<para>DESCRIBE - determine any dependencies</para>
@@ -2838,14 +4044,21 @@
<para>CLASSLOADER - create classloaders</para>
</listitem>
<listitem>
- <para>POST_CLASSLOADER - apply AOP changes together with any other bytecode manipulations</para>
+ <para>POST_CLASSLOADER - apply AOP changes together with any other bytecode
+ manipulations</para>
</listitem>
<listitem>
<para>REAL - process the deployment components</para>
</listitem>
</itemizedlist>
- <para>As the state machine controller takes a DeploymentControllerContext through each of these stages it calls the associated actions to perform the relevant work. For example we could create multiple parsing actions; one to parse XML files and another to parse Java property files. Depending on the file suffix of the deployment descriptor; .xml or .property we can then choose which one to use during the PARSE stage.</para>
- <para>Deployments are represented by DeploymentContexts which can be formed into a hierarchy to represent nested deployments of any depth. A reference to the top-level DeploymentContext is contained in the DeploymentControllerContext:</para>
+ <para>As the state machine controller takes a DeploymentControllerContext through each of
+ these stages it calls the associated actions to perform the relevant work. For example we
+ could create multiple parsing actions; one to parse XML files and another to parse Java
+ property files. Depending on the file suffix of the deployment descriptor; .xml or .property
+ we can then choose which one to use during the PARSE stage.</para>
+ <para>Deployments are represented by DeploymentContexts which can be formed into a hierarchy
+ to represent nested deployments of any depth. A reference to the top-level DeploymentContext
+ is contained in the DeploymentControllerContext:</para>
<programlisting role="JAVA">public class DeploymentControllerContext extends AbstractControllerContext
{
/** The deployment context */
@@ -2864,9 +4077,20 @@
}
}</programlisting>
<warning>
- <para>Be careful not to get confused with the names of these types; DeploymentControllerContext and DeploymentContext, as they are very similar. </para>
+ <para>Be careful not to get confused with the names of these types;
+ DeploymentControllerContext and DeploymentContext, as they are very similar. </para>
</warning>
- <para>As deployment descriptors do not always describe runtime components directly, the notion of a deployment component is defined. For example an EJB deployment descriptor may describe a number of EJB containers from which individual EJBs are created. Here we refer to both the EJB containers and EJBs as <emphasis>deployment components</emphasis> but only the EJBs are thought of as <emphasis>runtime components</emphasis>. Deployment components are represented by an implementation of DeploymentContext called ComponentDeploymentContext which allows them to be hierarchical to any depth. For example an EJB container ComponentDeploymentContext could have multiple child ComponentDeploymentContexts representing its EJBs. Deployment components are added/removed to and from their containing deployment or parent deployment component and retrieved for processing using the following methods:</para>
+ <para>As deployment descriptors do not always describe runtime components directly, the notion
+ of a deployment component is defined. For example an EJB deployment descriptor may describe
+ a number of EJB containers from which individual EJBs are created. Here we refer to both the
+ EJB containers and EJBs as <emphasis>deployment components</emphasis> but only the EJBs are
+ thought of as <emphasis>runtime components</emphasis>. Deployment components are represented
+ by an implementation of DeploymentContext called ComponentDeploymentContext which allows
+ them to be hierarchical to any depth. For example an EJB container
+ ComponentDeploymentContext could have multiple child ComponentDeploymentContexts
+ representing its EJBs. Deployment components are added/removed to and from their containing
+ deployment or parent deployment component and retrieved for processing using the following
+ methods:</para>
<programlisting role="JAVA">public class ComponentDeploymentContext implements DeploymentContext
{
...
@@ -2894,9 +4118,22 @@
...
}</programlisting>
- <para>The deployment framework is therefore a collection of packages and classes that provides a generic way to represent deployments containing arbitrary <emphasis>deployment components</emphasis>. These could be runtime components such as POJOs, MBeans, and EJBs or non-runtime components such as EJB containers which act as factories for the EJBs. Deployments and deployment components can be nested within one another to any depth providing maximum flexibility during deployment.</para>
- <para>Consequently it's possible to use the deployment framework instead of the BasicXMLDeployer to deploy POJO-based services specified with XML deployment descriptors. So why then do we need a BasicXMLDeployer? The answer is because the deployment framework itself a POJO-based service specified with an XML deployment descriptor (usually called <emphasis>bootstrap-beans.xml</emphasis>). We therefore need the BasicXMLDeployer to deploy it when the microcontainer is booted up. Once deployed then the framework is used to deploy everything else including any other POJO-based services.</para>
- <para>Now that you understand the reasoning for the deployment framework and how it relates to the BasicXMLDeployer the following sections go on the cover the various parts in more detail.</para>
+ <para>The deployment framework is therefore a collection of packages and classes that provides
+ a generic way to represent deployments containing arbitrary <emphasis>deployment
+ components</emphasis>. These could be runtime components such as POJOs, MBeans, and EJBs
+ or non-runtime components such as EJB containers which act as factories for the EJBs.
+ Deployments and deployment components can be nested within one another to any depth
+ providing maximum flexibility during deployment.</para>
+ <para>Consequently it's possible to use the deployment framework instead of the
+ BasicXMLDeployer to deploy POJO-based services specified with XML deployment descriptors. So
+ why then do we need a BasicXMLDeployer? The answer is because the deployment framework
+ itself a POJO-based service specified with an XML deployment descriptor (usually called
+ <emphasis>bootstrap-beans.xml</emphasis>). We therefore need the BasicXMLDeployer to
+ deploy it when the microcontainer is booted up. Once deployed then the framework is used to
+ deploy everything else including any other POJO-based services.</para>
+ <para>Now that you understand the reasoning for the deployment framework and how it relates to
+ the BasicXMLDeployer the following sections go on the cover the various parts in more
+ detail.</para>
</chapter>
<chapter>
<title>The Virtual File System</title>
@@ -2912,10 +4149,24 @@
<para>Resources - any other files that are required at runtime</para>
</listitem>
</itemizedlist>
- <para>Sometimes only a single deployment descriptor is required although more often you create a deployment archive containing one or more descriptors together with a number of classes. In the case of a Web Archive (WAR) we also include any resources that are to be hosted by the web server.</para>
- <para>During development it is convenient to be able to deploy such archives in an unpackaged rather than a packaged state. This means that files can be freely edited in their directories and the deployment processed again without having to re-package everything each time. Packaging is usually more appropriate during the test phase or when going to production as a way of keeping everything together.</para>
- <para>Depending on the layout of the development, test or production system it's helpful to allow the deployment of packaged or unpackaged archives both locally, on the computer's hard drive, or remotely over a network. Support for nested deployments is also important so that related items can be deployed together.</para>
- <para>To allow all of these concerns to be addressed in a maintainable and extendable way the deployment framework uses a Virtual File System (VFS). This provides a way to represent a deployment as a read-only hierarchical file system regardless of whether it is packaged or not. It also allows deployments to be accessed both locally and remotely using a pluggable design so that new protocols can easily be added to those supplied by default:</para>
+ <para>Sometimes only a single deployment descriptor is required although more often you create
+ a deployment archive containing one or more descriptors together with a number of classes.
+ In the case of a Web Archive (WAR) we also include any resources that are to be hosted by
+ the web server.</para>
+ <para>During development it is convenient to be able to deploy such archives in an unpackaged
+ rather than a packaged state. This means that files can be freely edited in their
+ directories and the deployment processed again without having to re-package everything each
+ time. Packaging is usually more appropriate during the test phase or when going to
+ production as a way of keeping everything together.</para>
+ <para>Depending on the layout of the development, test or production system it's helpful
+ to allow the deployment of packaged or unpackaged archives both locally, on the
+ computer's hard drive, or remotely over a network. Support for nested deployments is
+ also important so that related items can be deployed together.</para>
+ <para>To allow all of these concerns to be addressed in a maintainable and extendable way the
+ deployment framework uses a Virtual File System (VFS). This provides a way to represent a
+ deployment as a read-only hierarchical file system regardless of whether it is packaged or
+ not. It also allows deployments to be accessed both locally and remotely using a pluggable
+ design so that new protocols can easily be added to those supplied by default:</para>
<itemizedlist>
<listitem>
<para>File - </para>
@@ -2927,30 +4178,54 @@
<para>Memory - </para>
</listitem>
</itemizedlist>
- <para>Finally the VFS takes care of generating physical URLs for classloading and debugging. Logical URLs can also be generated for configuration purposes, or codebase protection domains.</para>
+ <para>Finally the VFS takes care of generating physical URLs for classloading and debugging.
+ Logical URLs can also be generated for configuration purposes, or codebase protection
+ domains.</para>
</chapter>
<chapter>
<title>Identifying the deployment structure</title>
- <para>Before we can begin to process a deployment we must first determine its structure. This means finding out if it contains any deployment descriptors and/or classes, and if so where they are located relative to the deployment's root. Nested deployments must also be detected and their structures determined in the same way. The purpose of this is to help the parsing actions locate the deployment descriptors in a standard way and to assist in the creation of classloaders. Since we wish to use the Virtual File System (VFS) to represent our deployment we must first create a VFSDeployment using the deployment URI or URL:</para>
+ <para>Before we can begin to process a deployment we must first determine its structure. This
+ means finding out if it contains any deployment descriptors and/or classes, and if so where
+ they are located relative to the deployment's root. Nested deployments must also be
+ detected and their structures determined in the same way. The purpose of this is to help the
+ parsing actions locate the deployment descriptors in a standard way and to assist in the
+ creation of classloaders. Since we wish to use the Virtual File System (VFS) to represent
+ our deployment we must first create a VFSDeployment using the deployment URI or URL:</para>
<programlisting role="JAVA">VirtualFile root = VFS.getRoot(deploymentURL);
VFSDeploymentFactory deploymentFactory = VFSDeploymentFactory.getInstance();
Deployment deployment = deploymentFactory.createVFSDeployment(root);</programlisting>
- <para>The deployment framework then determines the structure of the <emphasis>Deployment</emphasis> using classes that implement the <emphasis>StructureDeployer</emphasis> interface:</para>
+ <para>The deployment framework then determines the structure of the
+ <emphasis>Deployment</emphasis> using classes that implement the
+ <emphasis>StructureDeployer</emphasis> interface:</para>
<programlisting role="JAVA">public interface StructureDeployer extends Ordered
{
boolean determineStructure(VirtualFile root, VirtualFile parent, VirtualFile file, StructureMetaData metaData, VFSStructuralDeployers deployers) throws DeploymentException;
}</programlisting>
- <para>As a deployment can contain nested deployments the determineStructure() method is meant to be called recursively. The <emphasis>root</emphasis> parameter represents the root of the top-level deployment, the <emphasis>file</emphasis> parameter represents the root of the current (possibly nested) deployment being analyzed and the <emphasis>parent</emphasis> parameter its parent (or null if it is the top-level). The <emphasis>deployers</emphasis> parameter represents a class containing a list of all the <emphasis>StructureDeployer</emphasis> implementations and is needed in order to process any nested deployments.</para>
- <para>As each deployment is found a <emphasis>ContextInfo</emphasis> object is created to store the paths to any deployment descriptors and/or classes. These are then added to the <emphasis>StructureMetaData</emphasis> parameter so that the structure of the entire deployment is recorded. For example if we had a top-level deployment containing 2 nested deployments then we would create 3 <emphasis>ContextInfo</emphasis> objects, one for each deployment, and add these to the <emphasis>StructureMetaData</emphasis> object.</para>
+ <para>As a deployment can contain nested deployments the determineStructure() method is meant
+ to be called recursively. The <emphasis>root</emphasis> parameter represents the root of the
+ top-level deployment, the <emphasis>file</emphasis> parameter represents the root of the
+ current (possibly nested) deployment being analyzed and the <emphasis>parent</emphasis>
+ parameter its parent (or null if it is the top-level). The <emphasis>deployers</emphasis>
+ parameter represents a class containing a list of all the
+ <emphasis>StructureDeployer</emphasis> implementations and is needed in order to process
+ any nested deployments.</para>
+ <para>As each deployment is found a <emphasis>ContextInfo</emphasis> object is created to
+ store the paths to any deployment descriptors and/or classes. These are then added to the
+ <emphasis>StructureMetaData</emphasis> parameter so that the structure of the entire
+ deployment is recorded. For example if we had a top-level deployment containing 2 nested
+ deployments then we would create 3 <emphasis>ContextInfo</emphasis> objects, one for each
+ deployment, and add these to the <emphasis>StructureMetaData</emphasis> object.</para>
<section>
<title>Deployment Descriptors</title>
- <para>Deployment descriptors are files containing configuration information for deployments. They are detected using the <emphasis>FileStructure</emphasis> class by matching the file suffix or using a <emphasis>FileMatcher</emphasis>. Default file suffixes include:</para>
+ <para>Deployment descriptors are files containing configuration information for deployments.
+ They are detected using the <emphasis>FileStructure</emphasis> class by matching the file
+ suffix or using a <emphasis>FileMatcher</emphasis>. Default file suffixes include:</para>
<itemizedlist>
<listitem>
- <para>-beans.xml - contains bean definitions</para>
+ <para>-beans.xml - contains bean definitions</para>
</listitem>
<listitem>
- <para>-aop.xml - contains aspect definitions</para>
+ <para>-aop.xml - contains aspect definitions</para>
</listitem>
<listitem>
<para>-service.xml - contains mbean definitions</para>
@@ -2970,16 +4245,32 @@
<para>BshFileMatcher - checks whether the filename ends with .bsh (Bean Shell)</para>
</listitem>
</itemizedlist>
- <para>The purpose of the <emphasis>FileStructure</emphasis> class is to detect deployments that consist of a single deployment descriptor. As such it creates a single <emphasis>ContextInfo</emphasis> object when a known file is found and adds this to the <emphasis>StructureMetaData</emphasis>. Since no classes are present the list of classpaths within the <emphasis>ContextInfo</emphasis> is set to null.</para>
+ <para>The purpose of the <emphasis>FileStructure</emphasis> class is to detect deployments
+ that consist of a single deployment descriptor. As such it creates a single
+ <emphasis>ContextInfo</emphasis> object when a known file is found and adds this to the
+ <emphasis>StructureMetaData</emphasis>. Since no classes are present the list of
+ classpaths within the <emphasis>ContextInfo</emphasis> is set to null.</para>
<warning>
- <para>Currently any top-level deployment that consists of a single file is always identified as a known deployment descriptor. This is done to avoid having to specify all of the known file suffixes in a configuration file. The file suffix and FileMatcher checks are only performed for nested deployments.</para>
- <para>As the FileStructure class has a relative order of Integer.MAX_VALUE it is always called after any other <emphasis>StructureDeployer</emphasis> implementations such as JARStructure. This means that a deployment consisting of a single JAR file will be detected correctly. </para>
+ <para>Currently any top-level deployment that consists of a single file is always
+ identified as a known deployment descriptor. This is done to avoid having to specify all
+ of the known file suffixes in a configuration file. The file suffix and FileMatcher
+ checks are only performed for nested deployments.</para>
+ <para>As the FileStructure class has a relative order of Integer.MAX_VALUE it is always
+ called after any other <emphasis>StructureDeployer</emphasis> implementations such as
+ JARStructure. This means that a deployment consisting of a single JAR file will be
+ detected correctly. </para>
</warning>
- <para>Deployment descriptors that form part of a deployment together with other files, such as classes or resources are detected by other implementations of <emphasis>StructureDeployer</emphasis> such as<emphasis>JARStructure</emphasis> which is described next.</para>
+ <para>Deployment descriptors that form part of a deployment together with other files, such
+ as classes or resources are detected by other implementations of
+ <emphasis>StructureDeployer</emphasis> such as<emphasis>JARStructure</emphasis> which is
+ described next.</para>
</section>
<section>
<title>Java Archives (JARs)</title>
- <para>The <emphasis>JARStructure</emphasis> class determines whether a deployment, in the form of a file or a directory, represents a packaged or unpackaged JAR archive. If the deployment is a single file then the filename suffix is checked against a list contained in the <emphasis>JarUtils</emphasis> class:</para>
+ <para>The <emphasis>JARStructure</emphasis> class determines whether a deployment, in the
+ form of a file or a directory, represents a packaged or unpackaged JAR archive. If the
+ deployment is a single file then the filename suffix is checked against a list contained
+ in the <emphasis>JarUtils</emphasis> class:</para>
<itemizedlist>
<listitem>
<para>.zip - a standard archive</para>
@@ -3006,16 +4297,29 @@
<para>.aop - an aspect archive (defined by JBoss)</para>
</listitem>
</itemizedlist>
- <para>If the deployment is a directory then the same check is performed using the directory name. If a match is found in either case then the deployment is determined to represent a JAR archive.</para>
+ <para>If the deployment is a directory then the same check is performed using the directory
+ name. If a match is found in either case then the deployment is determined to represent a
+ JAR archive.</para>
<note>
- <para>A top-level directory whose name doesn't match any of the file suffixes is still considered an unpackaged JAR archive as this allows it to be used as a container for multiple nested deployments.</para>
+ <para>A top-level directory whose name doesn't match any of the file suffixes is
+ still considered an unpackaged JAR archive as this allows it to be used as a container
+ for multiple nested deployments.</para>
</note>
- <para>In order to detect nested JAR deployments we iterate through all subdirectories checking whether their names end in a known suffix or if they contain META-INF directories. The check for a META-INF directory is required otherwise every subdirectory without a known filename would be classified as a nested deployment.</para>
- <para>For each JAR deployment found we create a new <emphasis>ContextInfo</emphasis> object with its metadata path set to META-INF and the classpath set to the deployment root.</para>
+ <para>In order to detect nested JAR deployments we iterate through all subdirectories
+ checking whether their names end in a known suffix or if they contain META-INF
+ directories. The check for a META-INF directory is required otherwise every subdirectory
+ without a known filename would be classified as a nested deployment.</para>
+ <para>For each JAR deployment found we create a new <emphasis>ContextInfo</emphasis> object
+ with its metadata path set to META-INF and the classpath set to the deployment
+ root.</para>
</section>
<section>
<title>Nested deployments</title>
- <para>Nested deployments are found by calling the addAllChildren() method in <emphasis>AbstractStructureDeployer</emphasis> and passing in a reference to the deployment root and the current nested deployment along with the <emphasis>StructureMetaData</emphasis> and <emphasis>VFSStructuralDeployers</emphasis> references: </para>
+ <para>Nested deployments are found by calling the addAllChildren() method in
+ <emphasis>AbstractStructureDeployer</emphasis> and passing in a reference to the
+ deployment root and the current nested deployment along with the
+ <emphasis>StructureMetaData</emphasis> and <emphasis>VFSStructuralDeployers</emphasis>
+ references: </para>
<programlisting role="JAVA">public abstract class AbstractStructureDeployer implements StructureDeployer
{
...
@@ -3036,13 +4340,31 @@
...
}</programlisting>
- <para>This in turn calls the addChildren() method which creates a <emphasis>VirtualFileVisitor</emphasis> that is passed to the virtual file representing the current deployment. Each child file or directory of the current deploment is then visited using the visitor which use the <emphasis>VFSStructuralDeployers</emphasis> object to iterate through the list of <emphasis>StructureDeployers</emphasis>. These then determine whether the child represents a nested deployment. If a nested deployment is found then the process is repeated in order to recurse through the entire directory structure. This happens regardless of whether the deployment is packaged or unpackaged as the Virtual File System takes care of accessing the actual files.</para>
- <para>Due to this recursive algorithm you can freely nest any type of deployment within any other type of deployment to any level. There is one exection to this with WAR archives which cannot contain nested deployments. All you need to ensure is that you have defined a <emphasis>StructureDeployer</emphasis> implementation capable of recognising your deployment.</para>
+ <para>This in turn calls the addChildren() method which creates a
+ <emphasis>VirtualFileVisitor</emphasis> that is passed to the virtual file representing
+ the current deployment. Each child file or directory of the current deploment is then
+ visited using the visitor which use the <emphasis>VFSStructuralDeployers</emphasis> object
+ to iterate through the list of <emphasis>StructureDeployers</emphasis>. These then
+ determine whether the child represents a nested deployment. If a nested deployment is
+ found then the process is repeated in order to recurse through the entire directory
+ structure. This happens regardless of whether the deployment is packaged or unpackaged as
+ the Virtual File System takes care of accessing the actual files.</para>
+ <para>Due to this recursive algorithm you can freely nest any type of deployment within any
+ other type of deployment to any level. There is one exection to this with WAR archives
+ which cannot contain nested deployments. All you need to ensure is that you have defined a
+ <emphasis>StructureDeployer</emphasis> implementation capable of recognising your
+ deployment.</para>
</section>
<section>
<title>Declaring a custom deployment structure</title>
- <para>JBoss Microcontainer provides two <emphasis>StructureDeployer</emphasis> implementations out-of-the-box to recognise the most common forms of deployments; standalone deployment descriptors and packaged/unpackaged JAR archives. In addition it also includes a third implementation called <emphasis>DeclaredStructure</emphasis>.</para>
- <para><emphasis>DeclaredStructure</emphasis> allows you to specify the structure of a deployment, including any nested deployments, using an XML file called <emphasis>jboss-structure.xml</emphasis> placed in the deployment's META-INF directory. The contents of this file are:</para>
+ <para>JBoss Microcontainer provides two <emphasis>StructureDeployer</emphasis>
+ implementations out-of-the-box to recognise the most common forms of deployments;
+ standalone deployment descriptors and packaged/unpackaged JAR archives. In addition it
+ also includes a third implementation called <emphasis>DeclaredStructure</emphasis>.</para>
+ <para><emphasis>DeclaredStructure</emphasis> allows you to specify the structure of a
+ deployment, including any nested deployments, using an XML file called
+ <emphasis>jboss-structure.xml</emphasis> placed in the deployment's META-INF
+ directory. The contents of this file are:</para>
<programlisting role="XML"><structure>
<context>
<path name=""/>
@@ -3054,7 +4376,10 @@
</classpath>
</context>
</structure></programlisting>
- <para>This describes a top-level deployment and specifies that deployment descriptors can be found in the <emphasis>OTHER-DIR</emphasis> directory and classes in the root directory. If you want to describe nested deployments then you simply need to create additional <context> elements and specify their paths relative to the root deployment:</para>
+ <para>This describes a top-level deployment and specifies that deployment descriptors can be
+ found in the <emphasis>OTHER-DIR</emphasis> directory and classes in the root directory.
+ If you want to describe nested deployments then you simply need to create additional
+ <context> elements and specify their paths relative to the root deployment:</para>
<programlisting role="XML"><structure>
<context>
<path name=""/>
@@ -3086,27 +4411,67 @@
</classpath>
</context
</structure></programlisting>
- <para>Here we've added two nested deployments, one directly underneath the top-level deployment and another in a subdirectory called utils. The first nested deployment has deployment descriptors located in its <emphasis>descriptors/xml</emphasis> directory and classes in either the <emphasis>classes</emphasis> or <emphasis>thirdParty/classes</emphasis> directories. The second nested deployment has deployment descriptors in either the <emphasis>META-INF</emphasis> or <emphasis>config/descs</emphasis> directories and classes in the <emphasis>lib</emphasis> directory.</para>
+ <para>Here we've added two nested deployments, one directly underneath the top-level
+ deployment and another in a subdirectory called utils. The first nested deployment has
+ deployment descriptors located in its <emphasis>descriptors/xml</emphasis> directory and
+ classes in either the <emphasis>classes</emphasis> or
+ <emphasis>thirdParty/classes</emphasis> directories. The second nested deployment has
+ deployment descriptors in either the <emphasis>META-INF</emphasis> or
+ <emphasis>config/descs</emphasis> directories and classes in the
+ <emphasis>lib</emphasis> directory.</para>
<note>
- <para>In order to ensure that any JAR files that you add META-INF/jboss-structure.xml to are correctly recognised by <emphasis>DeclaredStructure</emphasis> instead of <emphasis>JARStructure</emphasis> the DeclaredStructure has a relativeOrder of 0 and the JARStructure a relativeOrder of 10000. This means that the DeclaredStructure class will be consulted first when determining the structure of a deployment.</para>
+ <para>In order to ensure that any JAR files that you add META-INF/jboss-structure.xml to
+ are correctly recognised by <emphasis>DeclaredStructure</emphasis> instead of
+ <emphasis>JARStructure</emphasis> the DeclaredStructure has a relativeOrder of 0 and
+ the JARStructure a relativeOrder of 10000. This means that the DeclaredStructure class
+ will be consulted first when determining the structure of a deployment.</para>
</note>
- <para>If you use the <emphasis>DeclaredStructure</emphasis> approach then you must specify all nested deployments in the <emphasis>META-INF/jboss-structure.xml</emphasis> file. This is because no recursion takes place once the structure has been determined. If you want the ability to nest other kinds of deployments within your custom deployment structure then it may be easier to create your own <emphasis>StructureDeployer</emphasis> implementation. Doing so will allow you to call the addAllChildren() method to perform the recursion. </para>
+ <para>If you use the <emphasis>DeclaredStructure</emphasis> approach then you must specify
+ all nested deployments in the <emphasis>META-INF/jboss-structure.xml</emphasis> file. This
+ is because no recursion takes place once the structure has been determined. If you want
+ the ability to nest other kinds of deployments within your custom deployment structure
+ then it may be easier to create your own <emphasis>StructureDeployer</emphasis>
+ implementation. Doing so will allow you to call the addAllChildren() method to perform the
+ recursion. </para>
</section>
<section>
<title>Determining the deployment structure programmatically</title>
- <para>Using the <emphasis>DeclaredStructure</emphasis> approach is convenient if you have an unusual deployment structure and don't mind adding the <emphasis>META-INF/jboss-structure.xml</emphasis> file to your deployment. However, if you have many deployments and want to avoid adding this file each time, or you want to nest arbitrary deployments inside your custom structure then you are better off creating your own <emphasis>StructureDeployer</emphasis> implementation.</para>
- <para>The easiest way to do this is by extending the <emphasis>AbstractStructureDeployer</emphasis> class which already implements the <emphasis>StructureDeployer</emphasis> interface:</para>
+ <para>Using the <emphasis>DeclaredStructure</emphasis> approach is convenient if you have an
+ unusual deployment structure and don't mind adding the
+ <emphasis>META-INF/jboss-structure.xml</emphasis> file to your deployment. However, if
+ you have many deployments and want to avoid adding this file each time, or you want to
+ nest arbitrary deployments inside your custom structure then you are better off creating
+ your own <emphasis>StructureDeployer</emphasis> implementation.</para>
+ <para>The easiest way to do this is by extending the
+ <emphasis>AbstractStructureDeployer</emphasis> class which already implements the
+ <emphasis>StructureDeployer</emphasis> interface:</para>
<programlisting role="JAVA">public abstract class AbstractStructureDeployer implements StructureDeployer
{
...
}</programlisting>
- <para>This includes helper methods to see if a VirtualFile represents a file or a directory and whether it corresponds to a top-level deployment. It also has methods to recurse through all of the files within a deployment to find nested deployments and to create <emphasis>ContextInfo</emphasis> objects for each one found. The only method that you need to implement is the one defined by the <emphasis>StructureDeployer</emphasis> interface:</para>
+ <para>This includes helper methods to see if a VirtualFile represents a file or a directory
+ and whether it corresponds to a top-level deployment. It also has methods to recurse
+ through all of the files within a deployment to find nested deployments and to create
+ <emphasis>ContextInfo</emphasis> objects for each one found. The only method that you
+ need to implement is the one defined by the <emphasis>StructureDeployer</emphasis>
+ interface:</para>
<programlisting role="JAVA">public interface StructureDeployer extends Ordered
{
boolean determineStructure(VirtualFile root, VirtualFile parent, VirtualFile file, StructureMetaData metaData, VFSStructuralDeployers deployers) throws DeploymentException;
}</programlisting>
- <para>If you recognise the structure of the deployment then you must create a <emphasis>ContextInfo</emphasis> object containing the details and add it to the <emphasis>StructureMetaData</emphasis>. If you extend AbstractStructureDeployer then you can use the createContext() and addClassPath() helper methods to help do this. You must then return true to prevent any other <emphasis>StructureDeployer</emphasis> from analysing the deployment again.</para>
- <para>If you want to control when your <emphasis>StructureDeployer</emphasis> is called you can use the <emphasis>relativeOrder</emphasis> property. By default this is set to <code>Integer.MAX_VALUE</code> in <emphasis>AbstractStructureDeployer</emphasis>. Implementations with a lower value are called before those with a higher value. If two values are the same then the return values of each implementation's toString() method are compared. The relativeOrder values for the out-of-the-box <emphasis>StructureDeployer</emphasis> implementations are:</para>
+ <para>If you recognise the structure of the deployment then you must create a
+ <emphasis>ContextInfo</emphasis> object containing the details and add it to the
+ <emphasis>StructureMetaData</emphasis>. If you extend AbstractStructureDeployer then you
+ can use the createContext() and addClassPath() helper methods to help do this. You must
+ then return true to prevent any other <emphasis>StructureDeployer</emphasis> from
+ analysing the deployment again.</para>
+ <para>If you want to control when your <emphasis>StructureDeployer</emphasis> is called you
+ can use the <emphasis>relativeOrder</emphasis> property. By default this is set to
+ <code>Integer.MAX_VALUE</code> in <emphasis>AbstractStructureDeployer</emphasis>.
+ Implementations with a lower value are called before those with a higher value. If two
+ values are the same then the return values of each implementation's toString() method
+ are compared. The relativeOrder values for the out-of-the-box
+ <emphasis>StructureDeployer</emphasis> implementations are:</para>
<itemizedlist>
<listitem>
<para>DeclaredStructure - 0</para>
@@ -3118,14 +4483,18 @@
<para>FileStructure - Integer.MAX_VALUE</para>
</listitem>
</itemizedlist>
- <para>This means that DeclaredStructure is always called first followed by JARStructure and finally FileStructure.</para>
+ <para>This means that DeclaredStructure is always called first followed by JARStructure and
+ finally FileStructure.</para>
</section>
</chapter>
<chapter>
<title>Creating Deployment Contexts</title>
<section>
<title>Introduction</title>
- <para>Once the structure of a deployment has been determined then the next step is to create a number of <emphasis>DeploymentContext</emphasis> objects. A <emphasis>DeploymentContext</emphasis> is required for each deployment, whether top-level or nested, in order to store information such as:</para>
+ <para>Once the structure of a deployment has been determined then the next step is to create
+ a number of <emphasis>DeploymentContext</emphasis> objects. A
+ <emphasis>DeploymentContext</emphasis> is required for each deployment, whether
+ top-level or nested, in order to store information such as:</para>
<itemizedlist>
<listitem>
<para>the location of any deployment descriptors - the metadata path</para>
@@ -3140,10 +4509,12 @@
<para>the deployment name</para>
</listitem>
<listitem>
- <para>the order it should be deployed in relative to other deployments - the relative order</para>
+ <para>the order it should be deployed in relative to other deployments - the relative
+ order</para>
</listitem>
<listitem>
- <para>a list of deployment types that it represents (including nested deployments) - the types</para>
+ <para>a list of deployment types that it represents (including nested deployments) - the
+ types</para>
</listitem>
<listitem>
<para>the state of the deployment - whether it is deployed or not</para>
@@ -3164,21 +4535,41 @@
<para>references to any nested (child) deployments or the parent deployment</para>
</listitem>
</itemizedlist>
- <para>Since the <emphasis>StructureMetaData</emphasis> object created using the <emphasis>StructureDeployer</emphasis> implementations already contains some of this information we subsequently pass it to a <emphasis>StructureBuilder</emphasis> together with a reference to the original <emphasis>Deployment</emphasis> to populate the deployment with a tree of DeploymentContexts:</para>
+ <para>Since the <emphasis>StructureMetaData</emphasis> object created using the
+ <emphasis>StructureDeployer</emphasis> implementations already contains some of this
+ information we subsequently pass it to a <emphasis>StructureBuilder</emphasis> together
+ with a reference to the original <emphasis>Deployment</emphasis> to populate the
+ deployment with a tree of DeploymentContexts:</para>
<programlisting role="JAVA">public interface StructureBuilder
{
DeploymentContext populateContext(Deployment deployment, StructureMetaData metaData) throws DeploymentException;
}</programlisting>
- <para>During this method call the information contained within the <emphasis>ContextInfo</emphasis> objects, found inside the <emphasis>StructureMetaData</emphasis>, is transferred to new instances of <emphasis>DeploymentContext</emphasis> that are assembled into a hierarchy. A reference to the top-level <emphasis>DeploymentContext</emphasis> is then returned.</para>
+ <para>During this method call the information contained within the
+ <emphasis>ContextInfo</emphasis> objects, found inside the
+ <emphasis>StructureMetaData</emphasis>, is transferred to new instances of
+ <emphasis>DeploymentContext</emphasis> that are assembled into a hierarchy. A reference
+ to the top-level <emphasis>DeploymentContext</emphasis> is then returned.</para>
</section>
<section>
<title>Accessing a deployment</title>
- <para>The deployment framework uses <emphasis>DeploymentContext</emphasis> objects to access the internal details of a deployment during processing. However, if you wish to access a deployment yourself then you should use a wrapper called <emphasis>DeploymentUnit</emphasis>.</para>
- <para>This prevents you from getting or setting the deployment state, changing the deployment classloader if it already exists, changing any parent/child relationships between nested deployments and accessing any exceptions that have occurred during deployment. You are also prevented from calling the visit() method that allows you to visit all of the contexts in the hierarchy or the cleanup() method that removes deployment metadata from the metadata repository.</para>
+ <para>The deployment framework uses <emphasis>DeploymentContext</emphasis> objects to access
+ the internal details of a deployment during processing. However, if you wish to access a
+ deployment yourself then you should use a wrapper called
+ <emphasis>DeploymentUnit</emphasis>.</para>
+ <para>This prevents you from getting or setting the deployment state, changing the
+ deployment classloader if it already exists, changing any parent/child relationships
+ between nested deployments and accessing any exceptions that have occurred during
+ deployment. You are also prevented from calling the visit() method that allows you to
+ visit all of the contexts in the hierarchy or the cleanup() method that removes deployment
+ metadata from the metadata repository.</para>
</section>
<section>
<title>Attaching extra information</title>
- <para>Since each instance of <emphasis>DeploymentContext</emphasis> contains generic deployment information we need a way to store and retrieve additional information generated during the deployment process. This is done using the <emphasis>DeploymentUnit</emphasis> wrapper which extends the <emphasis>MutableAttachments</emphasis> interface:</para>
+ <para>Since each instance of <emphasis>DeploymentContext</emphasis> contains generic
+ deployment information we need a way to store and retrieve additional information
+ generated during the deployment process. This is done using the
+ <emphasis>DeploymentUnit</emphasis> wrapper which extends the
+ <emphasis>MutableAttachments</emphasis> interface:</para>
<programlisting role="JAVA">public interface DeploymentUnit extends MutableAttachments
{
...
@@ -3200,8 +4591,14 @@
int getChangeCount();
void clearChangeCount();
}</programlisting>
- <para>As you can see the <emphasis>MutableAttachments</emphasis> operations allow you to store arbitrary objects (referred to as attachments) inside a map using a <emphasis>String</emphasis> for the key. For convenience you can pass in the type of the object instead of a string and the return value of type.getName() will be used as the key. If you pass in a type for the key, or you specify an expectedType argument, then the return value will be cast to an object of that type.</para>
- <para>If you wish to retrieve any objects then you can use operations from the <emphasis>Attachments</emphasis> interface:</para>
+ <para>As you can see the <emphasis>MutableAttachments</emphasis> operations allow you to
+ store arbitrary objects (referred to as attachments) inside a map using a
+ <emphasis>String</emphasis> for the key. For convenience you can pass in the type of the
+ object instead of a string and the return value of type.getName() will be used as the key.
+ If you pass in a type for the key, or you specify an expectedType argument, then the
+ return value will be cast to an object of that type.</para>
+ <para>If you wish to retrieve any objects then you can use operations from the
+ <emphasis>Attachments</emphasis> interface:</para>
<programlisting role="JAVA">public interface Attachments extends Serializable
{
Map<String, Object> getAttachments();
@@ -3216,8 +4613,12 @@
boolean hasAttachments();
}</programlisting>
- <para>Again if you pass a type for the key, or specify an expectedType argument, then the return value will be cast to an object of that type. In the case of the isAttachmentPresent() methods the return value is only true if both the object can be located using the key and it can be cast to the specified type.</para>
- <para><emphasis>DeploymentUnit</emphasis> also contains a convenience method that allows you to retrieve all objects of a particular type from the map of attachments:</para>
+ <para>Again if you pass a type for the key, or specify an expectedType argument, then the
+ return value will be cast to an object of that type. In the case of the
+ isAttachmentPresent() methods the return value is only true if both the object can be
+ located using the key and it can be cast to the specified type.</para>
+ <para><emphasis>DeploymentUnit</emphasis> also contains a convenience method that allows you
+ to retrieve all objects of a particular type from the map of attachments:</para>
<programlisting role="JAVA">public interface DeploymentUnit extends MutableAttachments
{
...
@@ -3227,16 +4628,38 @@
</section>
<section>
<title>Deployment components</title>
- <para>Deployments usually contain one or more runtime components in addition to configuration information and resource files. By runtime components we mean any classes that require a container in which to run. Containers take care of controlling a component's lifecycle in addition to providing common behaviours so that component developers do not have to do this themselves. By leveraging the work of the container, components allow you to concentrate on developing the business logic of your application without having to continually write code for common tasks. This typically reduces the time needed to develop systems with advanced behaviour.</para>
- <para>The simplest example of a component is a POJO. In this case the container controls when the POJO is created based on when a client performs a lookup. It can also take care of wiring different POJOs together based on information in a deployment descriptor so that the developer doesn't have to hard-code any relationships. This makes configuration of the runtime environment easier to change.</para>
- <para>Another example is an MBean. Here the container (an MBeanServer) takes care of routing all method calls through a bus so that clients never reference the MBean directly. This allows MBeans to be replaced at runtime without impacting any clients. It also allows the management interface of an MBean, the attributes and operations exposed to clients, to change at runtime without needing to redeploy it.</para>
- <para>A further example is an OSGi service bundle. Here the container ensures that the classes required by the bundle are available in the runtime before the service is started. It also takes care of making classes within the bundle available to others when deployed and removing them again when undeployed. This makes the runtime environment easier to construct and maintain with fewer classloading issues.</para>
- <para>In order to provide support for deploying different types of runtime components, JBoss Microcontainer requires that each one is represented by its own <emphasis>ComponentDeploymentContext</emphasis>:</para>
+ <para>Deployments usually contain one or more runtime components in addition to
+ configuration information and resource files. By runtime components we mean any classes
+ that require a container in which to run. Containers take care of controlling a
+ component's lifecycle in addition to providing common behaviours so that component
+ developers do not have to do this themselves. By leveraging the work of the container,
+ components allow you to concentrate on developing the business logic of your application
+ without having to continually write code for common tasks. This typically reduces the time
+ needed to develop systems with advanced behaviour.</para>
+ <para>The simplest example of a component is a POJO. In this case the container controls
+ when the POJO is created based on when a client performs a lookup. It can also take care
+ of wiring different POJOs together based on information in a deployment descriptor so that
+ the developer doesn't have to hard-code any relationships. This makes configuration
+ of the runtime environment easier to change.</para>
+ <para>Another example is an MBean. Here the container (an MBeanServer) takes care of routing
+ all method calls through a bus so that clients never reference the MBean directly. This
+ allows MBeans to be replaced at runtime without impacting any clients. It also allows the
+ management interface of an MBean, the attributes and operations exposed to clients, to
+ change at runtime without needing to redeploy it.</para>
+ <para>A further example is an OSGi service bundle. Here the container ensures that the
+ classes required by the bundle are available in the runtime before the service is started.
+ It also takes care of making classes within the bundle available to others when deployed
+ and removing them again when undeployed. This makes the runtime environment easier to
+ construct and maintain with fewer classloading issues.</para>
+ <para>In order to provide support for deploying different types of runtime components, JBoss
+ Microcontainer requires that each one is represented by its own
+ <emphasis>ComponentDeploymentContext</emphasis>:</para>
<programlisting role="JAVA">public class ComponentDeploymentContext implements DeploymentContext
{
...
}</programlisting>
- <para>This is similer to a normal <emphasis>DeploymentContext</emphasis> implementation but with some important differences:</para>
+ <para>This is similer to a normal <emphasis>DeploymentContext</emphasis> implementation but
+ with some important differences:</para>
<itemizedlist>
<listitem>
<para>Components always have a relative order of 0</para>
@@ -3248,7 +4671,7 @@
<para>Components always use the classloader of the containing deployment</para>
</listitem>
<listitem>
- <para>Components cannot change their parent once constructed</para>
+ <para>Components cannot change their parent once constructed</para>
</listitem>
<listitem>
<para>Components can contain other components</para>
@@ -3257,7 +4680,8 @@
<para>Components are never considered top-level deployments</para>
</listitem>
</itemizedlist>
- <para>In addition ComponentDeploymentContexts delegate to their parent <emphasis>DeploymentContext</emphasis> for information such as:</para>
+ <para>In addition ComponentDeploymentContexts delegate to their parent
+ <emphasis>DeploymentContext</emphasis> for information such as:</para>
<itemizedlist>
<listitem>
<para>simple name</para>
@@ -3287,12 +4711,23 @@
<para>get/setProblem</para>
</listitem>
</itemizedlist>
- <para>The reason we use the <emphasis>DeploymentContext</emphasis> interface is so that we can process components together with normal deployments. This ensures that when a deployment is deployed any components that it contains are deployed with it. Similarly undeploying a deployment causes its components to be undeployed at the same time.</para>
+ <para>The reason we use the <emphasis>DeploymentContext</emphasis> interface is so that we
+ can process components together with normal deployments. This ensures that when a
+ deployment is deployed any components that it contains are deployed with it. Similarly
+ undeploying a deployment causes its components to be undeployed at the same time.</para>
<important>
- <para>An additional motivation is that we can easily create separate ControllerContexts for each component which we can then process using the state machine. This allows us to perform a well-defined set of deployment actions to deploy/undeploy each component in and out of the runtime.</para>
- <para>Having separate ControllerContexts for each component also allows you to manage the components individually, possibly via an Admin console. Furthermore each component can have its own dependencies allowing fine-grained control of how the runtime is constructed and destructed. </para>
+ <para>An additional motivation is that we can easily create separate ControllerContexts
+ for each component which we can then process using the state machine. This allows us to
+ perform a well-defined set of deployment actions to deploy/undeploy each component in
+ and out of the runtime.</para>
+ <para>Having separate ControllerContexts for each component also allows you to manage the
+ components individually, possibly via an Admin console. Furthermore each component can
+ have its own dependencies allowing fine-grained control of how the runtime is
+ constructed and destructed. </para>
</important>
- <para>ComponentDeploymentContexts are added to a <emphasis>DeploymentUnit</emphasis> using the addComponent() method whenever a component is discovered during the deployment process:</para>
+ <para>ComponentDeploymentContexts are added to a <emphasis>DeploymentUnit</emphasis> using
+ the addComponent() method whenever a component is discovered during the deployment
+ process:</para>
<programlisting role="JAVA">public class AbstractDeploymentUnit extends AbstractMutableAttachments implements DeploymentUnit
{
...
@@ -3315,7 +4750,8 @@
...
}</programlisting>
- <para>As components are usually accessed with the <emphasis>DeploymentUnit</emphasis> interface they can also have attachments just like any other deployment:</para>
+ <para>As components are usually accessed with the <emphasis>DeploymentUnit</emphasis>
+ interface they can also have attachments just like any other deployment:</para>
<programlisting role="JAVA">public interface DeploymentUnit extends MutableAttachments
{
...
@@ -3329,11 +4765,17 @@
...
}</programlisting>
- <para>An attachment may well be metadata associated with the component. In this way the metadata can be processed using deployment actions as the component's <emphasis>ControllerContext</emphasis> passes through the state machine.</para>
+ <para>An attachment may well be metadata associated with the component. In this way the
+ metadata can be processed using deployment actions as the component's
+ <emphasis>ControllerContext</emphasis> passes through the state machine.</para>
</section>
<section>
<title>Deployment dependencies</title>
- <para>Sometimes a deployment may have dependencies on other deployments or on a runtime component such as a classloader. In these cases we need to be able to define the dependencies so that the state machine can decide whether or not to progress the deployment to the next stage. This is done using the following methods of the <emphasis>DeploymentUnit</emphasis> interface:</para>
+ <para>Sometimes a deployment may have dependencies on other deployments or on a runtime
+ component such as a classloader. In these cases we need to be able to define the
+ dependencies so that the state machine can decide whether or not to progress the
+ deployment to the next stage. This is done using the following methods of the
+ <emphasis>DeploymentUnit</emphasis> interface:</para>
<programlisting role="JAVA">public interface DeploymentUnit extends MutableAttachments
{
...
@@ -3342,7 +4784,8 @@
void addIDependOn(DependencyItem dependency);
void removeIDependOn(DependencyItem dependency);
}</programlisting>
- <para><emphasis>AbstractDeploymentUnit</emphasis> delegates these calls to the <emphasis>DeploymentContext</emphasis> implementation that it wraps:</para>
+ <para><emphasis>AbstractDeploymentUnit</emphasis> delegates these calls to the
+ <emphasis>DeploymentContext</emphasis> implementation that it wraps:</para>
<programlisting role="JAVA">public class AbstractDeploymentUnit extends AbstractMutableAttachments implements DeploymentUnit
{
...
@@ -3364,7 +4807,9 @@
...
}</programlisting>
- <para>This in turn delegates them to the corresponding <emphasis>ControllerContext</emphasis> implementation which is stored as an attachment:</para>
+ <para>This in turn delegates them to the corresponding
+ <emphasis>ControllerContext</emphasis> implementation which is stored as an
+ attachment:</para>
<programlisting role="JAVA">public class AbstractDeploymentContext extends ManagedObjectsWithTransientAttachmentsImpl implements DeploymentContext
{
...
@@ -3385,16 +4830,28 @@
...
}</programlisting>
- <para>If a <emphasis>ControllerContext</emphasis> object is not found within the deployment's attachments then the parent deployment is tried. In this way DependencyItems are added/removed to the underlying <emphasis>ControllerContext</emphasis> implementation for the deployment. </para>
+ <para>If a <emphasis>ControllerContext</emphasis> object is not found within the
+ deployment's attachments then the parent deployment is tried. In this way
+ DependencyItems are added/removed to the underlying <emphasis>ControllerContext</emphasis>
+ implementation for the deployment. </para>
</section>
</chapter>
<chapter>
<title>Processing a deployment</title>
<section>
<title>Introduction</title>
- <para>So far we have established the structure of a deployment using <emphasis>StructureDeployer</emphasis> classes and created a corresponding <emphasis>DeploymentContext</emphasis> using a <emphasis>StructureBuilder</emphasis>. Nested deployments have been dealt with by creating additional DeploymentContexts and adding these as children of the parent <emphasis>DeploymentContext</emphasis>.</para>
- <para>The next step in the deployment framework is to process the deployment. The objective being to extract any metadata from the deployment descriptors and create a classloader. Components must also be identified during this process and a <emphasis>ComponentDeploymentContext</emphasis> created for each one so that they can be deployed into the runtime.</para>
- <para>The <emphasis>Deployers</emphasis> interface defines the operations that need to take place:</para>
+ <para>So far we have established the structure of a deployment using
+ <emphasis>StructureDeployer</emphasis> classes and created a corresponding
+ <emphasis>DeploymentContext</emphasis> using a <emphasis>StructureBuilder</emphasis>.
+ Nested deployments have been dealt with by creating additional DeploymentContexts and
+ adding these as children of the parent <emphasis>DeploymentContext</emphasis>.</para>
+ <para>The next step in the deployment framework is to process the deployment. The objective
+ being to extract any metadata from the deployment descriptors and create a classloader.
+ Components must also be identified during this process and a
+ <emphasis>ComponentDeploymentContext</emphasis> created for each one so that they can be
+ deployed into the runtime.</para>
+ <para>The <emphasis>Deployers</emphasis> interface defines the operations that need to take
+ place:</para>
<programlisting role="JAVA">public interface Deployers
{
...
@@ -3407,7 +4864,9 @@
void checkStructureComplete(DeploymentContext... contexts) throws DeploymentException;
}</programlisting>
- <para>In the default implementation <emphasis>DeployersImpl</emphasis> the process() method creates new DeploymentControllerContexts for each <emphasis>DeploymentContext</emphasis> in the deploy list and installs them into the microcontainer controller:</para>
+ <para>In the default implementation <emphasis>DeployersImpl</emphasis> the process() method
+ creates new DeploymentControllerContexts for each <emphasis>DeploymentContext</emphasis>
+ in the deploy list and installs them into the microcontainer controller:</para>
<programlisting role="JAVA">public class DeployersImpl implements Deployers, ControllerContextActions
{
...
@@ -3467,8 +4926,15 @@
...
}</programlisting>
- <para>Notice that a reference to the <emphasis>DeploymentControllerContext</emphasis> is stored as an attachment to the <emphasis>DeploymentContext</emphasis> so that it can be retrieved later and passed to the controller.install() method.</para>
- <para>Undeployment happens in a similar way. First of all the DeploymentControllerContexts associated with the DeploymentContexts in the undeploy list are extracted into a separate list. The microcontainer controller is then used again to push them through the deployment states in reverse order. Finally the DeploymentControllerContexts are removed from the <emphasis>DeploymentContext</emphasis> attachments where they were initially stored and uninstalled from the controller:</para>
+ <para>Notice that a reference to the <emphasis>DeploymentControllerContext</emphasis> is
+ stored as an attachment to the <emphasis>DeploymentContext</emphasis> so that it can be
+ retrieved later and passed to the controller.install() method.</para>
+ <para>Undeployment happens in a similar way. First of all the DeploymentControllerContexts
+ associated with the DeploymentContexts in the undeploy list are extracted into a separate
+ list. The microcontainer controller is then used again to push them through the deployment
+ states in reverse order. Finally the DeploymentControllerContexts are removed from the
+ <emphasis>DeploymentContext</emphasis> attachments where they were initially stored and
+ uninstalled from the controller:</para>
<programlisting role="JAVA">public class DeployersImpl implements Deployers, ControllerContextActions
{
...
@@ -3550,22 +5016,30 @@
}
</programlisting>
<note>
- <para>Undeployments are actually processed before deployments to ensure that we don't end up in a situation where a deployment that depends on a subsequently undeployed deployment has to be undeployed again straight away.</para>
+ <para>Undeployments are actually processed before deployments to ensure that we don't
+ end up in a situation where a deployment that depends on a subsequently undeployed
+ deployment has to be undeployed again straight away.</para>
</note>
- <para>You should hopefully see from this breakdown that each deployment is processed by creating a <emphasis>DeploymentControllerContext</emphasis> that is then installed into the microcontainer controller and pushed through a series of deployment stages. What these stages are and how they are created is the subject of the next section.</para>
+ <para>You should hopefully see from this breakdown that each deployment is processed by
+ creating a <emphasis>DeploymentControllerContext</emphasis> that is then installed into
+ the microcontainer controller and pushed through a series of deployment stages. What these
+ stages are and how they are created is the subject of the next section.</para>
</section>
<section>
<title>Deployment stages</title>
- <para> By default the <emphasis>DeployersImpl</emphasis> class creates the following deployment stages on construction:</para>
+ <para> By default the <emphasis>DeployersImpl</emphasis> class creates the following
+ deployment stages on construction:</para>
<itemizedlist>
<listitem>
- <para>NOT_INSTALLED - the deployment is ready to be installed or has been uninstalled</para>
+ <para>NOT_INSTALLED - the deployment is ready to be installed or has been
+ uninstalled</para>
</listitem>
<listitem>
- <para>PARSE - deployment descriptors are parsed into deployment metadata</para>
+ <para>PARSE - deployment descriptors are parsed into deployment metadata</para>
</listitem>
<listitem>
- <para>DESCRIBE - dependencies on other deployments or runtime components are determined</para>
+ <para>DESCRIBE - dependencies on other deployments or runtime components are
+ determined</para>
</listitem>
<listitem>
<para>CLASSLOADER - a classloader for the deployment is created</para>
@@ -3580,7 +5054,8 @@
<para>INSTALLED - the deployment is fully deployed</para>
</listitem>
</itemizedlist>
- <para>Each <emphasis>DeploymentStage</emphasis> has a name and indicates which stages it comes before and after: </para>
+ <para>Each <emphasis>DeploymentStage</emphasis> has a name and indicates which stages it
+ comes before and after: </para>
<programlisting role="JAVA">public class DeploymentStage
{
private String name;
@@ -3591,7 +5066,9 @@
...
}</programlisting>
- <para>This allows a new <emphasis>ControllerState</emphasis> to be created for each <emphasis>DeploymentStage</emphasis> which can then be added in the correct place to the list of ControllerStates associated with the controller:</para>
+ <para>This allows a new <emphasis>ControllerState</emphasis> to be created for each
+ <emphasis>DeploymentStage</emphasis> which can then be added in the correct place to the
+ list of ControllerStates associated with the controller:</para>
<programlisting role="JAVA">public class DeployersImpl implements Deployers, ControllerContextActions
{
...
@@ -3659,19 +5136,20 @@
...
}</programlisting>
- <para>The DeploymentStages are also stored in a map, using the stage name as a key, to prevent duplicate stages from being added.</para>
+ <para>The DeploymentStages are also stored in a map, using the stage name as a key, to
+ prevent duplicate stages from being added.</para>
</section>
<section>
<title>Deployment actions</title>
- <para/>
+ <para />
</section>
<section>
<title>Ordering (Inputs and outputs)</title>
- <para/>
+ <para />
</section>
<section>
<title>Deploying components</title>
- <para/>
+ <para />
</section>
</chapter>
<chapter>
More information about the jboss-cvs-commits
mailing list