Class loading in AS7 is considerably different to previous versions of JBoss AS. Class loading is based on the JBoss Modules project. Instead of the more familiar hierarchical class loading environment, AS7's class loading is based on modules that have to define explicit dependencies on other modules. Deployments in AS7 are also modules, and do not have access to classes that are defined in jars in the application server unless an explicit dependency on those classes is defined.
Module names for top level deployments follow the format deployment.myarchive.war while sub deployments are named like deployment.myear.ear.mywar.war.
This means that it is possible for a deployment to import classes from another deployment using the other deployments module name, the details of how to add an explicit module dependency are explained below.
Even though in AS7 modules are isolated by default, as part of the deployment process some dependencies on modules defined by the application server are set up for you automatically. For instance, if you are deploying a Java EE application a dependency on the Java EE API's will be added to your module automatically. Similarly if your module contains a beans.xml file a dependency on Weld will be added automatically, along with any supporting modules that weld needs to operate.
For a complete list of the automatic dependencies that are added see this page.
Automatic dependencies can be excluded through the use of jboss-deployment-structure.xml.
A common source of errors in Java applications is including API classes in a deployment that are also provided by the container. This can result in multiple versions of the class being created and the deployment failing to deploy properly. To prevent this in AS7, module dependencies are added in a specific order that should prevent this situation from occurring.
In order of highest priority to lowest priority
The war is considered to be a single module, so classes defined in WEB-INF/lib are treated the same as classes in WEB-INF/classes. All classes packaged in the war will be loaded with the same class loader.
Ear deployments are multi-module deployments. This means that not all classes inside an ear will necessarily have access to all other classes in the ear, unless explicit dependencies have been defined. By default the EAR/lib directory is a single module, and every WAR or EJB jar deployment is also a separate module. Sub deployments (wars and ejb-jars) always have a dependency on the parent module, which gives them access to classes in EAR/lib, however they do not always have an automatic dependency on each other. This behaviour is controlled via the ear-subdeployments-isolated setting in the ee subsystem configuration:
<
subsystem
xmlns
=
"urn:jboss:domain:ee:1.0"
>
<
ear-subdeployments-isolated
>false</
ear-subdeployments-isolated
>
</
subsystem
>
|
By default this is set to false, which allows the sub-deployments to see classes belonging to other sub-deployments within the .ear.
For example, consider the following .ear deployment:
myapp.ear
|
|--- web.war
|
|--- ejb1.jar
|
|--- ejb2.jar
|
If the ear-subdeployments-isolated is set to false, then the classes in web.war can access classes belonging to ejb1.jar and ejb2.jar. Similarly, classes from ejb1.jar can access classes from ejb2.jar (and vice-versa).
The ear-subdeployments-isolated element value has no effect on the isolated classloader of the .war file(s). i.e. irrespective of whether this flag is set to true or false, the .war within a .ear will have an isolated classloader and other sub-deployments within that .ear will not be able to access classes from that .war. This is as per spec. |
If the ear-subdeployments-isolated is set to true then no automatic module dependencies between the sub-deployments are set up. User must manually setup the dependency with Class-Path entries, or by setting up explicit module dependencies.
Portability The Java EE specification says that portable applications should not rely on sub deployments having access to other sub deployments unless an explicit Class-Path entry is set in the MANIFEST.MF. So portable applications should always use Class-Path entry to explicitly state their dependencies. Class-Path: /X:/libs/libFile.jar |
It is also possible to override the ear-subdeployments-isolated element value at a per deployment level. See the section on jboss-deployment-structure.xml below. |
Dependencies: Manifest Entries
Deployments (or more correctly modules within a deployment) may set up dependencies on other modules by adding a Dependencies: manifest entry. This entry consists of a comma separated list of module names that the deployment requires. The available modules can be seen under the modules directory in the application server distribution. For example to add a dependency on javassist and apache velocity you can add a manifest entry as follows:
Dependencies: org.javassist export,org.apache.velocity export services,org.antlr
Each dependency entry may also specify some of the following parameters by adding them after the module name:
Adding a dependency to all modules in an EAR Using the export parameter it is possible to add a dependency to all sub deployments in an ear. If a module is exported from a Dependencies: entry in the top level of the ear (or by a jar in the ear/lib directory) it will be available to all sub deployments as well. |
To generate a MANIFEST.MF entry when using maven put the following in your pom.xml:
pom.xml
If your deployment is a jar you must use the maven-jar-plugin rather than the maven-war-plugin. |
It is also possible to add module dependencies on other modules inside the deployment using the Class-Path manifest entry. This can be used within an ear to set up dependencies between sub deployments, and also to allow modules access to additional jars deployed in an ear that are not sub deployments and are not in the EAR/libdirectory. If a jar in the EAR/lib directory references a jar via Class-Path: then this additional jar is merged into the parent ear's module, and is accessible to all sub deployments in the ear.
It is also possible to set up global modules, that are accessible to all deployments. This is done by modifying the configuration file (standalone/domain.xml).
For example, to add javassist to all deployments you can use the following XML:
<
subsystem
xmlns
=
"urn:jboss:domain:ee:1.0"
>
<
global-modules
>
<
module
name
=
"org.javassist"
slot
=
"main"
/>
</
global-modules
>
</
subsystem
>
|
Note that the slot field is optional and defaults to main.
jboss-deployment-structure.xml is a JBoss specific deployment descriptor that can be used to control class loading in a fine grained manner. It should be placed in the top level deployment, in META-INF (or WEB-INF for web deployments). It can do the following:
An example of a complete jboss-deployment-structure.xml file for an ear deployment is as follows:
<
jboss-deployment-structure
xmlns
=
"urn:jboss:deployment-structure:1.2"
>
<!-- Make sub deployments isolated by default, so they cannot see each others classes without a Class-Path entry -->
<
ear-subdeployments-isolated
>true</
ear-subdeployments-isolated
>
<!-- This corresponds to the top level deployment. For a war this is the war's module, for an ear -->
<!-- This is the top level ear module, which contains all the classes in the EAR's lib folder -->
<
deployment
>
<!-- exclude-subsystem prevents a subsystems deployment unit processors running on a deployment -->
<!-- which gives basically the same effect as removing the subsystem, but it only affects single deployment -->
<
exclude-subsystems
>
<
subsystem
name
=
"resteasy"
/>
</
exclude-subsystems
>
<!-- Exclusions allow you to prevent the server from automatically adding some dependencies -->
<
exclusions
>
<
module
name
=
"org.javassist"
/>
</
exclusions
>
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<
dependencies
>
<
module
name
=
"deployment.javassist.proxy"
/>
<
module
name
=
"deployment.myjavassist"
/>
<!-- Import META-INF/services for ServiceLoader impls as well -->
<
module
name
=
"myservicemodule"
services
=
"import"
/>
</
dependencies
>
<!-- These add additional classes to the module. In this case it is the same as including the jar in the EAR's lib directory -->
<
resources
>
<
resource-root
path
=
"my-library.jar"
/>
</
resources
>
</
deployment
>
<
sub-deployment
name
=
"myapp.war"
>
<!-- This corresponds to the module for a web deployment -->
<!-- it can use all the same tags as the <deployment> entry above -->
<
dependencies
>
<!-- Adds a dependency on a ejb jar. This could also be done with a Class-Path entry -->
<
module
name
=
"deployment.myear.ear.myejbjar.jar"
/>
</
dependencies
>
<!-- Set's local resources to have the lowest priority -->
<!-- If the same class is both in the sub deployment and in another sub deployment that -->
<!-- is visible to the war, then the Class from the other deployment will be loaded, -->
<!-- rather than the class actually packaged in the war. -->
<!-- This can be used to resolve ClassCastExceptions if the same class is in multiple sub deployments-->
<
local-last
value
=
"true"
/>
</
sub-deployment
>
<!-- Now we are going to define two additional modules -->
<!-- This one is a different version of javassist that we have packaged -->
<
module
name
=
"deployment.myjavassist"
>
<
resources
>
<
resource-root
path
=
"javassist.jar"
>
<!-- We want to use the servers version of javassist.util.proxy.* so we filter it out-->
<
filter
>
<
exclude
path
=
"javassist/util/proxy"
/>
</
filter
>
</
resource-root
>
</
resources
>
</
module
>
<!-- This is a module that re-exports the containers version of javassist.util.proxy -->
<!-- This means that there is only one version of the Proxy classes defined -->
<
module
name
=
"deployment.javassist.proxy"
>
<
dependencies
>
<
module
name
=
"org.javassist"
>
<
imports
>
<
include
path
=
"javassist/util/proxy"
/>
<
exclude
path
=
"/**"
/>
</
imports
>
</
module
>
</
dependencies
>
</
module
>
</
jboss-deployment-structure
>
|
The xsd for jboss-deployment-structure.xml is available at the github repo. |
Not all JDK classes are exposed to a deployment by default. If your deployment uses JDK classes that are not exposed you can get access to them using jboss-deployment-structure.xml with system dependencies:
<
jboss-deployment-structure
xmlns
=
"urn:jboss:deployment-structure:1.1"
>
<
deployment
>
<
dependencies
>
<
system
export
=
"true"
>
<
paths
>
<
path
name
=
"com/sun/corba/se/spi/legacy/connection"
/>
</
paths
>
</
system
>
</
dependencies
>
</
deployment
>
</
jboss-deployment-structure
>
|
As explained in the ClassLoading in AS7 article, JBoss AS7 is based on module classloading. A class within a module B isn't visible to a class within a module A, unless module B adds a dependency on module A. Module dependencies can be explicitly (as explained in that classloading article) or can be "implicit". This article will explain what implicit module dependencies mean and how, when and which modules are added as implicit dependencies.
Consider an application deployment which contains EJBs. EJBs typically need access to classes from the javax.ejb.* package and other Java EE API packages. The jars containing these packages are already shipped in JBoss AS and are available as "modules". The module which contains the javax.ejb.* classes has a specific name and so does the module which contains all the Java EE API classes. For an application to be able to use these classes, it has to add a dependency on the relevant modules. Forcing the application developers to add module dependencies like these (i.e. dependencies which can be "inferred") isn't a productive approach. Hence, whenever an application is being deployed, the deployers within the server, which are processing this deployment "implicitly" add these module dependencies to the deployment so that these classes are visible to the deployment at runtime. This way the application developer doesn't have to worry about adding them explicitly. How and when these implicit dependencies are added is explained in the next section.
When a deployment is being processed by the server, it goes through a chain of "deployment processors". Each of these processors will have a way to check if the deployment meets a certain criteria and if it does, the deployment processor adds a implicit module dependency to that deployment. Let's take an example - Consider (again) an EJB3 deployment which has the following class:
@Stateless
public
class
MySuperDuperBean {
...
}
|
As can be seen, we have a simple @Stateless EJB. When the deployment containing this class is being processed, the EJB deployment processor will see that the deployment contains a class with the @Stateless annotation and thus identifies this as a EJB deployment. This is just one of the several ways, various deployment processors can identify a deployment of some specific type. The EJB deployment processor will then add an implicit dependency on the Java EE API module, so that all the Java EE API classes are visible to the deployment.
Some subsystems will always add a API classes, even if the trigger condition is not met. These are listed separately below.
In the next section, we'll list down the implicit module dependencies that are added to a deployment, by various deployers within JBoss AS.
Subsystem responsible for adding the implicit dependency | Dependencies that are always added | Dependencies that are added if a trigger condition is met |
Trigger which leads to the implicit module dependency being added |
---|---|---|---|
Core Server |
|
||
EE Subsystem |
|
||
EJB3 subsystem |
|
The presence of ejb-jar.xml (in valid locations in the deployment, as specified by spec) or the presence of annotation based EJBs (ex: @Stateless, @Stateful, @MessageDriven etc) | |
JAX-RS (Resteasy) subsystem |
|
|
The presence of JAX-RS annotations in the deployment |
JCA sub-system |
|
|
If the deployment is a resource adaptor (RAR) deployment. |
JPA (Hibernate) subsystem |
|
|
The presence of an @PersistenceUnit or @PersistenceContext annotation, or a <persistence-unit-ref> or <persistence-context-ref> in a deployment descriptor. . |
SAR Subsystem |
|
The deployment is a SAR archive | |
Security Subsystem |
|
||
Web Subsystem |
|
The deployment is a WAR archive. JSF is only added if used. Multiple version options exist for mojarra. | |
Web Services Subsystem |
|
||
Weld (CDI) Subsystem |
|
If a beans.xml file is detected in the deployment |
The purpose of this guide is to document only those changes that are needed to successfully run and deploy AS 5 applications on AS 7.It provides information on to resolve deployment and runtime problems and how to prevent changes in application behaviour. This is the first step in moving to the new platform. Once the application is successfully deployed and running on the new platform, plans can be made to upgrade individual components to use the new functions and features of AS7.
JBoss AS7 is structured differently from previous versions of JBoss AS, so you may want do a little research before you attempt to migrate your application. Getting Started with JBoss Application Server 7 in the Getting Started Guide has a lot of useful information, including a table that lists the features that are supported in AS 7.0. If your application uses features that go beyond the Java Enterprise Edition 6 Web Profile specification, you want to make sure you go with AS 7.1 or later (this is when AS7 becameEE6 Full Profile compliant). You should also read Getting started with JBoss AS for helpful information about how to install and run the application server.
The rest of this document describes some of the changes you might need to make to your application to successfully deploy and run it on AS7.
Reminder Before making any modifications to your application, make sure to create a backup copy. |
Class loading in AS7 is considerably different than in previous versions of JBoss AS. Class loading is now based on the JBoss Modules project. Instead of the more familiar hierarchical class loading environment, AS7's class loading is based on modules that have to define explicit dependencies on other modules. Deployments in AS7 are also modules, and do not have access to classes that are defined in jars in the application server unless an explicit dependency on those classes is defined.
Even though in AS7 modules are isolated by default, as part of the deployment process some dependencies on modules defined by the application server are set up for you automatically. For instance, if you are deploying a Java EE application, a dependency on the Java EE API's will be added to your module automatically. Similarly if your module contains a beans.xml file, a dependency on Weld will be added automatically, along with any supporting modules that weld needs to operate. For a complete list of the automatic dependencies that are added see Implicit module dependencies for deployments.
The deployers within the server "implicitly" add some commonly used module dependencies, like the javax.api and sun.jdk, to the deployment so that the classes are visible to the deployment at runtime. This way the application developer doesn't have to worry about adding them explicitly. How and when these implicit dependencies are added is explained in Implicit module dependencies for deployments.
For some classes, the modules must be specified explicitly in the MANIFEST.MF as Dependencies: or "Class-Path:" entries or in the jboss-deployment-structure.xml. Otherwise you may see ClassNotFoundExceptions, NoClassDefFoundErrors, or ClassCastExceptions. Tattletale is an excellent tool that can help you identify module dependencies in your application. Tattletale is described later in this document here: Use Tattletale to find application dependencies.
You may also see runtime errors and page redirects to the JBoss Seam Debug Page. Manifest entries are explained in more detail here: Class Loading in AS7.
When you migrate your application, you may have to change the packaging structure of your EAR or WAR due to the class loading changes.
A WAR is a single module and all classes in the WAR are loaded with the same class loader. This means classes packaged in the WEB-INF/lib are treated the same as classes in WEB-INF/classes.
An EAR is different in that it consists of multiple modules. The EAR/lib directory is a single module, and every WAR or EJB jar deployment is also a separate module. Classes will not necessarily have access to other classes in the EAR unless explicit dependencies have been defined.
For more information on EAR and WAR packaging, see "WAR Class Loading" and "EAR Class Loading" in Class Loading in AS7.
Reminder If you are using maven configure defaultLibBundleDir property to lib directory:
|
If you have defined a class-loading element, you need to remove it. The behaviour that this provokes in EAP 5 is now the default class-loading behaviour in AS 7, so it is no longer necessary. If you do not remove this element, you will see a ParseError and XMLStreamException in your server log:
javax.xml.stream.XMLStreamException: ParseError at [row,col]:[
5
,
5
]
Message: Unexpected element
'class-loading'
encountered
at org.jboss.metadata.parser.util.MetaDataElementParser.unexpectedElement(MetaDataElementParser.java:
109
)
at org.jboss.metadata.parser.jbossweb.JBossWebMetaDataParser.parse(JBossWebMetaDataParser.java:
182
)
at org.jboss.as.web.deployment.JBossWebParsingDeploymentProcessor.deploy(JBossWebParsingDeploymentProcessor.java:
64
)
...
5
more
|
Depending on which components your application uses, you may need to add one or more dependencies to this file. There is more information about these dependencies in the following paragraphs.
The following is an example of MANIFEST.MF file that has been modified to contain dependencies and classpath entries:
Manifest-Version:
1.0
Dependencies: org.jboss.logmanager
Class-Path: OrderManagerEJB.jar
|
If you modify the MANIFEST.MF file, make sure to include a newline character at the end of the file. |
If you use Maven, you may need to modify your pom.xml file to generate the dependencies for the MANIFEST.MF file.
If your application uses EJB 3.0, you may have a section in the pom.xml file that looks like the following:
<
plugin
>
<
groupId
>org.apache.maven.plugins</
groupId
>
<
artifactId
>maven-ejb-plugin</
artifactId
>
<
configuration
>
<
ejbVersion
>3.0</
ejbVersion
>
</
configuration
>
</
plugin
>
|
If the EJB 3.0 code uses org.apache.commons.log, you will need to add that dependency to the MANIFEST.MF file. To do that, you will need to modify the plugin element in the pom.xml file as follows:
<
plugin
>
<
groupId
>org.apache.maven.plugins</
groupId
>
<
artifactId
>maven-ejb-plugin</
artifactId
>
<
configuration
>
<
ejbVersion
>3.0</
ejbVersion
>
<
archive
>
<
manifestFile
>src/main/resources/META-INF/MANIFEST.MF</
manifestFile
>
</
archive
>
</
configuration
>
</
plugin
>
|
In the above example, the src/main/resourcres/MANIFEST.MF file only needs to contain the dependency entry:
Dependencies: org.apache.commons.logging
|
Maven will generate the complete MANIFEST.MF file:
Manifest-Version:
1.0
Dependencies: org.apache.commons.logging
|
This file is a JBoss specific deployment descriptor that can be used to control class loading in a fine grained manner. Like the MANIFEST.MF, this file can be used to add dependencies. If can also prevent automatic dependencies from being added, define additional modules, change an EAR deployment's isolated class loading behaviour, and add additional resource roots to a module.
For additional information, see jboss-deployment-structure.xml.
In previous versions of JBoss AS, you controlled the order of deployments within an EAR through the use of the jboss-app.xml file. This is no longer the case. The Java EE6 spec provides the <initialize-in-order> element in the application.xml which allows control of the order in which the Java EE modules within an EAR are deployed.
In most cases you do not need to specify deployment order. If your application uses dependency injections and resource-refs to refer to components in external modules, in most cases the <initialize-in-order> element is not required because the application server is able to implicitly determine the correct and optimal way of ordering the components.
Assume you have an application that contains a myBeans.jar and a myApp.war within a myApp.ear. A servlet in the myApp.war @EJB injects a bean from myBeans.jar. In this case, the application server has the appropriate knowledge to make sure that the EJB component is available before the servlet is started and you do not have to use the <initialize-in-order> element.
However, if that servlet uses legacy JNDI lookup style remote references like the following to access the bean, you may need to specify module order.
init() {
Context ctx =
new
InitialContext();
ctx.lookup(
"TheBeanInMyBeansModule"
);
}
|
In this case, the server is not able to determine that that EJB component is in the myBeans.jar and you need to enforce that the components in the myBeans.jar are initialized and started before the components in myApp.war. To do this, you set the <initialize-in-order> element to true and specify the order of the myBeans.jar andmyApp.war modules in the application.xml file.
The following is an example that uses the <initialize-in-order> element to control deployment order. The myBeans.jar is deployed before the myApp.war.
<
application
xmlns
=
"http://java.sun.com/xml/ns/javaee"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
version
=
"6"
xsi:schemaLocation
=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
>
<
application-name
>jboss-as-ejb-in-ear</
application-name
>
<
initialize-in-order
>true</
initialize-in-order
>
<
module
>
<
ejb
>myBeans.jar</
ejb
>
</
module
>
<
module
>
<
web
>
<
web-uri
>myApp.war</
web-uri
>
<
context-root
>myApp</
context-root
>
</
web
>
</
module
>
</
application
>
|
The schema for the application.xml file can be found here at http://java.sun.com/xml/ns/javaee/application_6.xsd.
You should be aware that setting <initialize-in-order> to true slows down deployment. It is preferable to define proper dependencies using dependency injections or resource-refs since it allows the container more flexibility in optimizing deployments. |
In previous versions of AS, the JBOSS_HOME/server/<servername>/conf/ was available in the classpath. Hence the properties files in that location were available in the classpath of the application.
In AS7, to get those properties available in the classpath, package them within your application. For example, if you are deploying a .war then package those properties in WAR WEB-INF/classes/ folder. If you want those properties accessible to all components in a .ear, then package them at the root of some .jar and place that jar in EARlib/ folder.
For more information about the class loading changes in AS7, see Class Loading in AS7.
For more information about modular class loading, see the Module Compatible Classloading Guide.
For information about deployment module dependencies, see Deployment Module Dependencies.
In previous versions of the application server, the JCA data source configuration was defined in a file with a suffix of *-ds.xml. This file was then deployed in the server's deploy directory. The JDBC driver was copied to the server lib/ directory or packaged in the application's WEB-INF/lib/ directory.
In AS7, this has all changed. You will no longer package the JDBC driver with the application or in the server/lib directory. The *-ds.xml file is now obsolete and the datasource configuration information is now defined in the standalone/configuration/standalone.xml or in the domain/configuration/domain.xml file. The IronJacamar distribution features a migration tool that can help you convert your previous datasource configuration file into the new format. You can find more information about the tool here: IronJacamar Datasource and Resource Adapter Migration Tool .
A JDBC 4-compliant driver can be installed as a deployment or as a core module. A driver that is JDBC 4-compliant contains a META-INF/services/java.sql.Driver file that specifies the driver class name. A driver that is not JDBC 4-compliant requires additional steps, as noted below.
In AS7, a datasource is configured in the server configuration file. If you are running in domain mode, the configuration file is the domain/configuration/domain.xml file. If you are running in standalone mode, you will configure the datasource in the standalone/configuration/standalone.xml file. Schema reference information, which is the same for both modes, can be found here: Datasource Descriptors.
First, you will need to create a datasource element and a driver element for your JDBC driver and datasource information in the standalone.xml or domain.xml file. You will use some of the same information that was previously defined in the *-ds.xml file.
The following is an example of a MySQL datasource element:
<
datasource
jndi-name
=
"java:/YourDatasourceName"
pool-name
=
"YourDatasourceName"
>
<
connection-url
>jdbc:mysql://localhost:3306/YourApplicationURL</
connection-url
>
<
driver
> mysql-connector-java-5.1.15.jar </
driver
>
<
transaction-isolation
> TRANSACTION_READ_COMMITTED </
transaction-isolation
>
<
pool
>
<
min-pool-size
>100</
min-pool-size
>
<
max-pool-size
>200</
max-pool-size
>
</
pool
>
<
security
>
<
user-name
> USERID </
user-name
>
<
password
> PASSWORD</
password
>
</
security
>
<
statement
>
<
prepared-statement-cache-size
>100</
prepared-statement-cache-size
>
<
share-prepared-statements
/>
</
statement
>
</
datasource
>
|
A JAR that is JDBC 4-compliant contains a META-INF/services/java.sql.Driver file that specifies the driver class name. This is used by the server to find name of the class(es) of the Drivers which exist in that JAR.
This is an example of the driver element for a JDBC 4-compliant MySQL driver.
<
driver
name
=
"mysql-connector-java-5.1.15.jar"
module
=
"com.mysql"
/>
|
This is an example of the driver element for driver that is not JDBC 4-compliant. The driver-class must be specified since it there is no META-INF/services/java.sql.Driverfile that specifies the driver class name.
<
driver
name
=
"mysql-connector-java-5.1.15.jar"
module
=
"com.mysql"
>
<
driver-class
>com.mysql.jdbc.Driver</
driver-class
>
</
driver
>
|
For more information on how to add a MySQL driver, see Adding a MySQL datasource to JBoss AS 7.
The JDBC driver can be installed into the container in one of two ways: either as a deployment or as a core module. There are pros and cons to each approach, which will be outlined below. For a detailed explanation how to deploy JDBC 4-compliant driver jar, please refer to the DataSources chapter of the Admin Guide.
When you install the JDBC driver as a deployment, it is deployed as a regular JAR. This is the recommended way to install the driver. When you run your application server in domain mode, deployments are automatically propagated to all servers to which the deployment applies; thus distribution of the driver JAR is one less thing for administrators to worry about.
Any JDBC 4-compliant driver will automatically be recognized and installed into the system by name and version. A JDBC 4-compliant JAR is identified using the Java service provider mechanism. It contains a text a file named "META-INF/services/java.sql.Driver", which contains the name of the class(es) of the Drivers which exist in that JAR. If your JDBC driver JAR is not JDBC 4-compliant, it can be made deployable by adding the java.sql.Driver file to the JAR or by deploying the JAR with an overlay. More information on that topic can be found here: Installing a JDBC Driver as a Deployment.
In AS7 standalone mode, you simply copy the JDBC 4-compliant JAR into the AS7_HOME/standalone/deployments directory. Here is an example of a MySQL JDBC driver installed as a deployment:
AS7_HOME/standalone/deployments/mysql-connector-java-5.1.15.jar
If the JDBC driver consists of more than one JAR, for example a JAR with the driver and a dependent license JAR, you can not install the driver as a deployment. You must install the JDBC driver as a core module. |
To install the driver as a module, you will need to create a directory structure under the modules folder. This structure will contain the driver and a module.xml file to define the module.
For example, using the MySQL JDBC driver above, you would create a directory structure as follows:
AS7_HOME/modules/com/mysql/main
In the main directory, you then create the following module.xml file:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2010, Red Hat, Inc., and individual contributors as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<
module
xmlns
=
"urn:jboss:module:1.0"
name
=
"com.mysql"
>
<
resources
>
<
resource-root
path
=
"mysql-connector-java-5.1.15.jar"
/>
</
resources
>
<
dependencies
>
<
module
name
=
"javax.api"
/>
</
dependencies
>
</
module
>
|
The module name, "com.mysql", matches the directory structure for this module. Under dependencies, you specify the module's dependencies on other modules. In this case, as the case with all JDBC data sources, it is dependent on the Java JDBC APIs which are defined in another module named javax.api that is located under modules/javax/api/main.
Make sure you do NOT have a space at the beginning of module.xml file or you will get a "New missing/unsatisfied dependencies" error for this driver. |
If your application uses JPA and currently bundles the Hibernate JARs, you may want to use the Hibernate that is included with AS7. You should remove the Hibernate jars from your application. You should also remove the "hibernate.transaction.manager_lookup_class" property from your persistence.xml as this is not needed.
DataSource Configuration in AS7
DataSource Descriptors
Admin Guide - DataSource Configuration
How to create and manage datasources in AS7
Adding a MySQL datasource to JBoss AS 7.
TODO: The following section on Resource adapters needs to be reviewed
In previous versions of the application server, the resource adapter configuration was defined in a file with a suffix of *-ds.xml.
In AS7, a resource adapter is configured in the server configuration file. If you are running in domain mode, the configuration file is the domain/configuration/domain.xmlfile. If you are running in standalone mode, you will configure the resource adapter in the standalone/configuration/standalone.xml file. Schema reference information, which is the same for both modes, can be found here: Resource adapter descriptors. The IronJacamar distribution features a migration tool that can help you convert your previous datasource configuration file into the new format. You can find more information about the tool here: IronJacamar Datasource and Resource Adapter Migration Tool .
The resource adapter descriptor information is defined under the <subsystem xmlns="urn:jboss:domain:resource-adapters:1.0"/> element in the server configuration file. You will use some of the same information that was previously defined in the *-ds.xml file.
The following is an example of a resource adapter element in the server configuration file:
<
resource-adapters
>
<
resource-adapter
>
<
archive
>multiple-full.rar</
archive
>
<
config-property
name
=
"Name"
>ResourceAdapterValue</
config-property
>
<
transaction-support
>NoTransaction</
transaction-support
>
<
connection-definitions
>
<
connection-definition
class-name
=
"org.jboss.jca.test.deployers.spec.rars.multiple.MultipleManagedConnectionFactory1"
enabled
=
"true"
jndi-name
=
"java:/eis/MultipleConnectionFactory1"
pool-name
=
"MultipleConnectionFactory1"
>
<
config-property
name
=
"Name"
>MultipleConnectionFactory1Value</
config-property
>
</
connection-definition
>
<
connection-definition
class-name
=
"org.jboss.jca.test.deployers.spec.rars.multiple.MultipleManagedConnectionFactory2"
enabled
=
"true"
jndi-name
=
"java:/eis/MultipleConnectionFactory2"
pool-name
=
"MultipleConnectionFactory2"
>
<
config-property
name
=
"Name"
>MultipleConnectionFactory2Value</
config-property
>
</
connection-definition
>
</
connection-definitions
>
<
admin-objects
>
<
admin-object
class-name
=
"org.jboss.jca.test.deployers.spec.rars.multiple.MultipleAdminObject1Impl"
jndi-name
=
"java:/eis/MultipleAdminObject1"
>
<
config-property
name
=
"Name"
>MultipleAdminObject1Value</
config-property
>
</
admin-object
>
<
admin-object
class-name
=
"org.jboss.jca.test.deployers.spec.rars.multiple.MultipleAdminObject2Impl"
jndi-name
=
"java:/eis/MultipleAdminObject2"
>
<
config-property
name
=
"Name"
>MultipleAdminObject2Value</
config-property
>
</
admin-object
>
</
admin-objects
>
</
resource-adapter
>
</
resource-adapters
>
|
TODO: The above section on Resource adapters needs to be reviewed
EJB 3.1 introduced a standardized global JNDI namespace and a series of related namespaces that map to the various scopes of a Java EE application. The three JNDI namespaces used for portable JNDI lookups are java:global, java:module, and java:app. If you use JNDI lookups in your application, you will need to change them to follow the new standardized JNDI namespace convention.
To conform to the new portable JNDI namespace rules, you will need to review the JNDI namespace rules and modify the application code to follow these rules.
AS7 has tightened up on JNDI namespace names to provide predictable and consistent rules for every name bound in the application server and to prevent future compatibility issues. This means you might run into issues with the current namespaces in your application if they don't follow the new rules.
Namespaces should follow these rules:
Here's an example of a JNDI lookup in EAP 5.1. This code is usually found in an initialization method. Note the lookup name is: "OrderManagerApp/ProductManagerBean/local".
private
ProductManager productManager;
try
{
context =
new
InitialContext();
productManager = (ProductManager) context.lookup(
"OrderManagerApp/ProductManagerBean/local"
);
}
catch
(Exception lookupError) {
throw
new
ServletException(
"Unable to find the ProductManager bean"
, lookupError);
}
|
Here is an example of how the same lookup would be coded in AS7. This code is defined as member variables. The lookup name now uses the new portable java:app JNDI namespace: "java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager"
@EJB
(lookup=
"java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager"
)
private
ProductManager productManager;
|
(This needs input!)
Previous Namespace | New Namespaces |
---|---|
OrderManagerApp/ProductManagerBean/local | java:module/ProductManagerBean!services.ejb.ProductManager (EE6 standard binding, only accessible within the same module) |
OrderManagerApp/ProductManagerBean/local | java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager (EE6 standard binding, only accessible within the same application) |
OrderManagerApp/ProductManagerBean/local | java:global/OrderManagerApp/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager (EE6 standard binding, globally accessible) |
java:comp/UserTransaction | java:comp/UserTransaction (This will not be accessible for non EE threads, e.g. Threads your application directly creates) |
java:comp/UserTransaction | java:jboss/UserTransaction (Globally accessible, use this if java:comp/UserTransaction is not available) |
java:/TransactionManager | java:jboss/TransactionManager |
java:/TransactionSynchronizationRegistry | java:jboss/TransactionSynchronizationRegistry |
Java EE6 Tutorial - Portable JNDI Syntax
Application-specified Portable JNDI Names
TODO: complete this section
In AS 7, there are two ways to make remote invocations to the server:
This section covers option 2: coding changes required for clients that use JNDI.
In EAP 5, the EJB remote interface was bound in JNDI, by default, under the name "ejbName/local" for local interfaces, and "ejbName/remote" for the remote interfaces. The client application then looked up the stateful bean using "ejbName/remote".
In AS 7, you use the ejb: namespace for remote access to EJBs with the following syntax:
For stateless beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-
interface
>
|
For stateful beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-
interface
>?stateful
|
The values to be substituted in the above syntax are:
Assume you have deployed the following stateless EJB to an AS 7 server. Note that it exposes a remote view for the bean.
@Stateless
@Remote
(RemoteCalculator.
class
)
public
class
CalculatorBean
implements
RemoteCalculator {
@Override
public
int
add(
int
a,
int
b) {
return
a + b;
}
@Override
public
int
subtract(
int
a,
int
b) {
return
a - b;
}
}
|
In EAP 5, the client EJB lookup and invocation was coded something like this:
InitialContext ctx =
new
InitialContext();
RemoteCalculator calculator = (RemoteCalculator)
ctx.lookup(
"CalculatorBean/remote"
);
int
a =
204
;
int
b =
340
;
int
sum = calculator.add(a, b));
|
In AS 7, using the information described above, the client lookup and invocation is coded like this:
final
Hashtable jndiProperties =
new
Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES,
"org.jboss.ejb.client.naming"
);
final
Context context =
new
InitialContext(jndiProperties);
final
String appName =
""
;
final
String moduleName =
"jboss-as-ejb-remote-app"
;
final
String distinctName =
""
;
final
String beanName = CalculatorBean.
class
.getSimpleName();
final
String viewClassName = RemoteCalculator.
class
.getName();
final
RemoteCalculator statelessRemoteCalculator = RemoteCalculator) context.lookup(
"ejb:"
+ appName +
"/"
+ moduleName +
"/"
+ distinctName +
"/"
+ beanName +
"!"
+ viewClassName);
int
a =
204
;
int
b =
340
;
int
sum = statelessRemoteCalculator.add(a, b);
|
If your client is accessing a stateful EJB, you must append “?stateful” to the end of the context lookup like this:
final
RemoteCalculator statelessRemoteCalculator = RemoteCalculator) context.lookup(
"ejb:"
+ appName +
"/"
+ moduleName +
"/"
+ distinctName +
"/"
+ beanName +
"!"
+ viewClassName +
"?stateful"
);
|
A complete working example, including both server and client code, can be found at the github repo here https://github.com/jbossas/quickstart/tree/master/ejb-remote.
For more information on remote invocations using JNDI, see EJB invocations from a remote client using JNDI.
JBoss LogManager supports front ends for all logging frameworks, so you can keep your current logging code or move to the new JBoss logging infrastructure. Regardless of your decision, because of the modular class loading changes, you will probably need to modify your application to add the required dependencies.
As of 7.1.0.Final logging dependencies are added by default. There is one caveat to this. If you're using log4j and you don't want to use the logging subsystem to configure your handlers (appenders), then you need to exclude the server copy of log4j. See the logging documentation
To use the new framework, you will need the change your imports and code as follows to achieve the same results as above:
import
org.jboss.logging.Level;
import
org.jboss.logging.Logger;
private
static
final
Logger logger = Logger.getLogger(MyClass.
class
.toString());
if
(logger.isTraceEnabled()) {
logger.tracef(
"Starting..."
, subsystem);
}
|
The JAR containing the JBoss Logging classes is located in the module named "org.jboss.logging". Your MANIFEST-MF file will look like this:
Dependencies: org.jboss.logging
For more information on how to find the module dependency, please see “How to Resolve ClassNotFoundExceptions” under the “Modular Class Loading” above.
JBoss AS7 Logging
JBoss Logging Tooling
If your application contains a persistence.xml file or the code uses @PersistenceContext or @PersistenceUnit, AS7 will detect this during deployment and assume the application uses JPA. It will implicitly add Hibernate 4 and a few other dependencies to your application classpath.
If your application currently uses Hibernate 3 libraries, in most cases, this will not be a problem. You will be able to deploy and run successfully using Hibernate 4. However, if you see a ClassNotFoundExceptions when deploying your application you can try one of two following approaches:
For more information on Hibernate and JPA, see JPA Reference Guide.
In Hibernate 4.0 there is a new IdentifierGenerator implementations.
Hibernate 4.0 supplies a property that is called hibernate.id.new_generator_mappings and hibernate documentation states that it defaults to false for backward compatibility reasons - however in AS 7.1 this value defaults to true.
consider setting the value to false in persistence.xml in case you don't see the behaviour you had in your app in previous versions.
for more information - read about 5.1.2 Identifiers in general and about 5.1.2.2. Identifier generator more specifically* *here
To comply with the JPA 2.0 specification section 7.6.3.1 requirements for persistence context propagation, jira AS7-1663 (An extended persistence context should not be propagated if there is no JTA transaction) was fixed in AS 7.02. If a SFSB with an extended persistence context, invokes another bean (not in an active JTA transaction) and the other bean uses a transactional entity manager. The extended persistence context will not be propagated to the transactional entity manager (propagation will occur if invoked with an active JTA transaction). If your application expects the extended persistence context to be propagated to the bean with the transactional entity manager, you need to change your app to do the invocation with an active JTA transaction.
JBoss Cache has been replaced by Infinispan for 2nd level cache (even for non-clustered use). This requires a change to the persistence.xml file. The syntax is slightly different, depending on if you're using JPA or Hibernate APIs. These examples assume you are using JPA.
Here's an example of the persistence.xml file in EAP 5.1:
<
property
name
=
"hibernate.cache.region.factory_class"
value
=
"org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"
/>
<
property
name
=
"hibernate.cache.region.jbc2.cachefactory"
value
=
"java:CacheManager"
/>
<
property
name
=
"hibernate.cache.use_second_level_cache"
value
=
"true"
/>
<
property
name
=
"hibernate.cache.use_minimal_puts"
value
=
"true"
/>
<
property
name
=
"hibernate.cache.region.jbc2.cfg.entity"
value
=
"mvcc-entity"
/>
<
property
name
=
"hibernate.cache.region_prefix"
value
=
"services"
/>
|
Here is what is needed to configure the same thing for a JPA application using Infinispan with AS7:
<property name=
"hibernate.cache.use_second_level_cache"
value=
"true"
/>
<property name=
"hibernate.cache.use_minimal_puts"
value=
"true"
/>
|
Here is what is needed to configure the same thing for a native Hibernate application using Infinispan with AS7:
<
property
name
=
"hibernate.cache.region.factory_class"
value
=
"org.hibernate.cache.infinispan.JndiInfinispanRegionFactory"
/>
<
property
name
=
"hibernate.cache.infinispan.cachemanager"
value
=
"java:jboss/infinispan/hibernate"
/>
<
property
name
=
"hibernate.transaction.manager_lookup_class"
value
=
"org.hibernate.transaction.JBossTransactionManagerLookup"
/>
<
property
name
=
"hibernate.cache.use_second_level_cache"
value
=
"true"
/>
<
property
name
=
"hibernate.cache.use_minimal_puts"
value
=
"true"
/>
|
Since JPA applications use Infinispan by default, fewer properties are needed to be specified. For native Hibernate applications you need to:
Migrating from Hibernate Validator 3.x to 4.x means switching to a completely new code base and new API - the JSR 303 (Bean Validation). The good news is that this migration should be straight forward. Let's have a look at the different parts which needs updating.
AS7 binds a default ValidatorFactory to the JNDI context under the name java:comp/ValidatorFactory.
When used in combination with Hibernate 4 life-cycle based validation will be automatically enabled by Hibernate Core. Validation will occur on entity INSERT, UPDATE and DELETE operations. You can configure the groups to be validated per event type using the properties javax.persistence.validation.group.pre-persist,javax.persistence.validation.group.pre-update, and javax.persistence.validation.group.pre-remove. The values of these properties are the comma-separated, fully specified class names of the groups to validate. The validation groups feature is part of the Bean Validation specification and it is recommended you get familiar with this new functionality. For a pure migration however, you can ignore groups as Hibernate Validator 3 did not have such a feature.
You can also disable life-cycle based validation by setting the javax.persistence.validation.mode to none. Other values are auto, callback and ddl, where auto is the default.
In case you want to manually validate, you need to create a Validator instance from the ValidatorFactory via ValidatorFactory.getValidator. or get Validator injected in your EJB, CDI bean or any other Java EE injectable resource. You can customize your Validator instance by using a fluent API starting with ValidatorFactory.usingContext. Using this API you can configure a custom MessageInterpolator, TraverableResolver and ConstraintValidatorFactory. All these interfaces are new to Hibernate Validator and are specified in the Bean Validation specification.
Once you have your Validator instance, use the validate methods to either validate your whole entity or just a single property. A call to any validate method will return a set ofConstraintViolation instances (in contrast to an array of InvalidValue instances in Validator 3). An empty set indicates a successful validation without any constraint violations.
This is the most visible change fro Hibernate Validator 3 constraints in your code base. To upgrade to Hibernate Validator 4 you have to use the constraints in thejavax.validation.constraints and org.hibernate.validator.constraints packages. The former contains the standardized Bean Validation constraints whereas the latter contains some Hibernate Validator specific constraints. All constraints which existed in Validator 3 are available in Validator 4. You just need to import the right class and in some cases change the name and/or type of a constraint parameter.
In Validator 3 a custom constraint needed to implement org.hibernate.validator.Validator. In Validator 4 the interface to implement is javax.validation.ConstraintValidator which contains the same methods as the old interface, namely initialize and isValid. Only the method signature is slightly different. Note that support for DDL alteration is no longer part of Hibernate Validator 4 but very few users made use of this feature.
Infinispan
Using Infinspan as JPA/Hibernate Second Level Cache Provider
HIBERNATE - RelationalPersistence for Idiomatic Java
The UsersRolesLoginModule has always looked for the properties files in the classpath. In previous versions of AS, files in the JBOSS_HOME/server/SERVER_NAME/confdirectory were on classpath so you could put your properties files there. In AS7 the directory structure has changed, so you need to package the properties within the application to make them available in the classpath.
To configure security for basic authentication, add a new security domain under security-domains to your standalone.xml configuration file :
<security-domain name="example"> <authentication> <login-module code="UsersRoles" flag="required"> <module-option name="usersProperties" value="${jboss.server.config.dir}/example-users.properties"/> <module-option name="rolesProperties" value="${jboss.server.config.dir}/example-roles.properties"/> </login-module> </authentication> </security-domain> |
If you are running in standalone mode, ${jboss.server.config.dir} refers to JBOSS_HOME/standalone/configuration/
A list of available login modules can be found in the admin guide.
In AS7 security domains no longer use the prefix java:/jaas/ in their names. Remove this prefix from the security domain configurations in jboss-web.xml for web applications and jboss.xml for enterprise applications.
AS7 sets up Resteasy for you, so there is no need to set it up yourself.
You should remove all of the resteasy configuration from your web.xml and replace it with one of the following three options:
@ApplicationPath
(
"/mypath"
)
public
class
MyApplication
extends
Application {
}
|
This will make your JAX-RS resources available under /mywebappcontext/mypath.
Note that that the path is /mypath not /mypath/* |
public
class
MyApplication
extends
Application {
}
|
<
servlet-mapping
>
<
servlet-name
>com.acme.MyApplication</
servlet-name
>
<
url-pattern
>/hello/*</
url-pattern
>
</
servlet-mapping
>
|
This will make your JAX-RS resources available under /mywebappcontext/hello.
You can also use this approach to override an application path set with the @ApplicationPath annotation. |
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
|
This will make your JAX-RS resources available under /mywebappcontext/hello.
Note that you only have to add the mapping, not the corresponding servlet. The server is responsible for adding the corresponding servlet automatically. |
For more information, see the JAX-RS Reference Guide.
In previous releases of JBoss AS, the LDAP realm was configured as an application-policy in the login-config.xml file. The following is an example of configuration in a previous release:
<
application-policy
name
=
"mcp_ldap_domain"
>
<
authentication
>
<
login-module
code
=
"org.jboss.security.auth.spi.LdapExtLoginModule"
flag
=
"required"
>
<
module-option
name
=
"java.naming.factory.initial"
>com.sun.jndi.ldap.LdapCtxFactory</
module-option
>
<
module-option
name
=
"java.naming.security.authentication"
>simple</
module-option
>
....
</
login-module
>
</
authentication
>
</
application-policy
>
|
In AS7, the LDAP realm is configured as a security-domain in the standalone/configuration/standalone.xml file if you are running a standalone server or in thedomain/configuration/domain.xml file if you are running your server in a managed domain. The following is an example of configuration in AS7:
<
subsystem
xmlns
=
"urn:jboss:domain:security:1.0"
>
<
security-domains
>
<
security-domain
name
=
"mcp_ldap_domain"
type
=
"default"
>
<
authentication
>
<
login-module
code
=
"org.jboss.security.auth.spi.LdapLoginModule"
flag
=
"required"
>
<
module-option
name
=
"java.naming.factory.initial"
value
=
"com.sun.jndi.ldap.LdapCtxFactory"
>
<
module-option
name
=
"java.naming.security.authentication"
value
=
"simple"
>
...
</
module-option
>
</
module-option
>
</
login-module
>
</
authentication
>
</
security-domain
>
</
security-domains
>
</
subsystem
>
|
The XML parser has also changed in AS7. In previous releases, you specified the module options as element content like this:
In AS7, the module options must be specified as element attributes with "value=" as follows:
|
JBoss Messaging will no longer be included in EAP 6. If your application uses JBoss Messaging as the messaging provider, you will need to make changes to your application to use HornetQ.
Transfer the existing JBoss Messaging configurations to the AS7 configuration. These configurations are located in deployment descriptors on the server running JBoss Messaging. This includes the following configurations:
Refer to Messaging configuration for details on HornetQ configurations.
If the application code uses standard JMS, no code changes are required. However, if the application will be connecting to a cluster (which is outside the scope of the JMS specification) please carefully review the HornetQ documentation on clustering semantics as HornetQ and JBoss Messaging have taken substantially different approaches in their respective implementations of clustering functionality.
If the application code uses features specific to JBoss Messaging, modify the code to use equivalent features available in HornetQ (if available).
Move any messages in the JBoss Messaging database to the HornetQ journal via a JMS bridge. Instructions for this are available here.
For more information on HornetQ, see HornetQ Project Documentation and the HornetQ homepage.
To enable clustering in AS5 and AS6, you needed to start your server instances using the "all" profile (or some derivation of it).
e.g.
$ ./bin/run.sh -c all
In AS7, the method of enabling clustering depends on whether your servers are started via the domain controller, or as standalone servers.
To enable clustering for servers started via the domain controller, update your domain.xml and designate a server group to use the "ha" profile and "ha-sockets" socket binding group:
e.g.
<
server-groups
>
<
server-group
name
=
"main-server-group"
profile
=
"ha"
>
<
jvm
name
=
"default"
>
<
heap
size
=
"64m"
max-size
=
"512m"
/>
</
jvm
>
<
socket-binding-group
ref
=
"ha-sockets"
/>
</
server-group
>
</
server-group
>
|
To enable clustering for standalone servers, start the server using the appropriate configuration file:
$ ./bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME
In AS5 and AS6, you would typically indicate the bind address used for clustering via the "-b" command line argument.
e.g.
$ ./bin/run.sh -c all -b 192.168.0.2
In AS7, bind addresses are explicitly defined by the relevant socket bindings within the AS7 configuration files. For servers started via the domain controller, bind addresses are specified within domain/configuration/host.xml. For standalone servers, bind addresses are specified within the standalone-ha.xml:
e.g.
<
interfaces
>
<
interface
name
=
"management"
>
<
inet-address
value
=
"192.168.0.2"
/>
</
interface
>
<
interface
name
=
"public"
>
<
inet-address
value
=
"192.168.0.2"
/>
</
interface
>
</
interfaces
>
|
<
socket-binding-groups
>
<
socket-binding-group
name
=
"ha-sockets"
default-interface
=
"public"
>
<!-- ... -->
</
socket-binding-group
>
</
socket-binding-groups
>
|
In the example above, the "public" interface is specified as the default interface for all sockets within the "ha-sockets" socket binding group.
In previous releases, the web server jvmRoute was configured using a property in the server.xml file. In AS 7, the jvmRoute attribute is configured in the web subsystem of the server configuration file using the instance-id attribute as follows:
subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false" instance-id="{JVM_ROUTE_SERVER}">
|
The JVM_ROUTE_SERVER above should be replaced by the jvmRoute server ID.
In AS5 and AS6, you could specify the multicast address and port used for intra-cluster communication using the command line arguments "-u" and "-m", respectively.
e.g.
./bin/run.sh -c all -u 228.11.11.11 -m 45688
In AS7, the multicast address and port used for intra-cluster communication are defined by the socket-binding referenced by the relevant JGroups protocol stack.
e.g.
<
subsystem
xmlns
=
"urn:jboss:domain:jgroups:1.0"
default-stack
=
"udp"
>
<
stack
name
=
"udp"
>
<
transport
type
=
"UDP"
socket-binding
=
"jgroups-udp"
/>
<!-- ... -->
</
stack
>
</
subsystem
>
|
<
socket-binding-groups
>
<
socket-binding-group
name
=
"ha-sockets"
default-interface
=
"public"
>
<!-- ... -->
<
socket-binding
name
=
"jgroups-udp"
port
=
"55200"
multicast-address
=
"228.11.11.11"
multicast-port
=
"45688"
/>
<!-- ... -->
</
socket-binding-group
>
</
socket-binding-groups
>
|
If you prefer to specify the multicast address and port in the command line, you can define the multicast address and ports as system properties and then use those properties on the command line when you start the server. In the following example, "jboss.mcast.addr" is the variable for the multicast address and "jboss.mcast.port" is the variable for the port.
<
socket-binding
name
=
"jgroups-udp"
port
=
"55200"
multicast-address
=
"${jboss.mcast.addr:230.0.0.4}"
multicast-port
=
"${jboss.mcast.port:45688}"
/>
|
You can then start your server using the following command line arguments:
$ ./bin/domain.sh -Djboss.mcast.addr=228.11.11.11 -Djboss.mcast.port=45688
In AS5 & AS6, you could manipulate the default protocol stack used for all clustering services via the "jboss.default.jgroups.stack" system property.
e.g.
./bin/run.sh -c all -Djboss.default.jgroups.stack=tcp
In AS7, the default protocol stack is defined by the JGroups subsystem within domain.xml or standalone-ha.xml.
e.g.
<
subsystem
xmlns
=
"urn:jboss:domain:jgroups:1.0"
default-stack
=
"udp"
>
<
stack
name
=
"udp"
>
<!-- ... -->
</
stack
>
</
subsystem
>
|
TODO: This topic needs content
The jboss-maven-plugin has not been updated and does not work In AS7. You must use org.jboss.as.plugins:jboss-as-maven-plugin to deploy to the correct directory.
Although AS 7 no longer uses service-style descriptors, the container supports these service-style deployments without change where possible. This means that if you usedjboss-service.xml or jboss-beans.xml deployment descriptors in your AS5/AS6 application, they should run with little or no modification in AS 7. You can continue to package the files in the EAR or SAR, or you can place the files directly in the deployments directory. If you are running a standalone server, the deployments directory is located here: JBOSS_HOME/standalone/deployments/.
ClassNotFoundExceptions can occur due to application packaging issues, unresolved dependencies, or missing archives. If the class specified is a class you have created in your application, the problem is mostly likely a packaging issue. If the class is is external to your project, you may need to explicitly define the dependencies on other modules or possibly copy an archive from a previous framework.
If the class specified in the exception is a class written specifically for the application, for example, a business class, you may need to change the packaging of the application. Due to modular class loading changes, your application may no longer be able to find classes within the EAR or WAR. In his case, you may need to move JARs to a different location within the application archive or modify class-path information so classes can be found. For more information on EAR and WAR packaging, see "WAR Class Loading" and "EAR Class Loading" in Class Loading in AS7.
For other classes, to resolve the dependency you will find the JAR that contains the class specified by the ClassNotFoundException by looking in the JBoss AS7 modules directory. If you find a module for the class, you must add a dependency to the manifest entry.
For example, if you see this ClassNotFoundException trace in the log:
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.Log from [Module "deployment.TopicIndex.war:main" from Service Module Loader]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:188)
Find the JBoss module containing this class by doing one of the following:
Dependencies: org.apache.commons.logging
|
Dependencies: org.dom4j,org.slf4j.jcl-over-slf4j
|
If the class is not found in a JAR packaged in a module defined by the AS7 server, find the JAR in your EAP 5.x install or your prior server's lib directory and copy it to your application's lib/ directory.
For example, when you migrate the Seam Booking appliation from EAP 5.1, you see this ClassNotFoundException in the log:
Caused by: java.lang.NoClassDefFoundError: org/hibernate/validator/ClassValidator
at java.lang.Class.getDeclaredMethods0(Native Method) [:1.6.0_25]
grep
'org.hibernate.validator.ClassValidator r'
`find . \-name
'*.jar'
`
|
This usually happens because the class is being loaded by a different class loader than the class it extends. It can also be a result of the same class existing in multiple JARs.
First find the JAR that contains the class and determine how it is being loaded. Often you need to find and remove the JAR from the application's WAR or EAR. Then you must find the dependent JBoss module containing the class, and explicitly define the dependency in the MANIFEST.MF file.
There are cases where you will need to copy in older JARs for your applcation. If the newer JARs are loaded as automatic dependencies in AS7, you may see ClassCastExceptions and need to exclude the conflicting module dependencies. This will prevent the server from adding the dependencies. This is an example of theexclusions element in the jboss-deployment-structure.xml file:
<
exclusions
>
<
module
name
=
"org.javassist"
/>
</
exclusions
>
|
If you are not able to figure out how the classes are loaded, you can often resolve the problem by printing class loader information to the log. For example, if you see the following ClassCastException in the log:
java.lang.ClassCastException: com.example1.Foo1 cannot be cast to com.example2.Foo2
|
In your code, print the class loader information by logging
logger.info(
"Class loader for foo1: "
+ com.example1.Foo1.getClass().getClassLoader().toString());
logger.info(
"Class loader for foo2: "
+ com.example2.Foo2.getClass().getClassLoader().toString());
|
The ModuleClassLoader information in the log will show which modules are loading the classes and, based on your application, you will need to determine the best approach to resolve the issue. You might have to remove or move a conflicting JARs, add dependencies through the MANIFEST.MF or jboss-deployment-structure.xml file or excluding dependencies in the jboss-deployment-structure.xml file.
A NoSuchMethodException indicates a class version mismatch between JAR files. It indicates your application's calling class was compiled using a different version of the JAR than the one used by the application server runtime. This can occur when you package different versions of common libraries your application, for example Hibernate. This can also happen if you migrate between versions of JBoss EAP and do not recompile your application against the updated EAP jars. To resolve this problem, remove any common JARs from your application archive, add any dependencies as described above, recompile and deploy your application.
If you get a DuplicateServiceException for a subdeployment of a JAR or a message that the WAR application has already been installed when you deploy your EAR in AS7, it may be due to the way JBoss WS handles the deployment. In AS6, JBossWS introduced a Context Root Mapping Algorithm or rules for servlet based endpoints to allow it to become seamlessly compatible with TCK6. This means that if you have a WAR and a JAR with the same name within an EAR, it may create a web context with the same name as the JAR which will conflict with the WAR context. You can resolve this issue in one of the following ways:
Usually the root cause of the exception can be found in one of the links on this page, for example, under the Component org.jboss.seam.caughtException. Use the same technique above under “How to Resolve ClassNotFoundExceptions or NoCLassDefFoundErrors” to resolve the dependencies.
As 7.1 provides support for EJB 2.1, however, you need to make a few code modifications and must start the server with the full profile.
Like EJB 3.0, you must use the full JNDI prefix with EJB 2.1. For more information on the new JNDI namespace rules and code examples, see the section 'Update application JNDI namespace names' above.
JBoss AOP (Aspect Oriented Programming) is no longer included in JBoss AS 7. In previous releases, JBoss AOP was used by the EJB container. However, in AS 7, the EJB container uses a new mechanism. If your application uses JBoss AOP, you need modify your application code as follows.
Modify the <jndi-name> for each <ejb-ref> to use the new JNDI fully qualified lookup format.
The jboss-ejb3.xml deployment descriptor replaces the jboss.xml deployment descriptor to override and add to the features provided by the Java Enterprise Edition (EE) defined ejb3-jar.xml deployment descriptor. The new file is incompatible with jboss.xml, and the jboss.xml is now ignored in deployments.
EJB 2.1 requires the Java Enterprise Edition 6 Full Profile. To start AS 7 with the full profile, pass the argument "-c standalone-full.xml" on the command line when you start the server.
ToDo: Conplete this section
When you migrate a Seam 2 application, you will follow the steps outlined in the migration guide. You will need to configure the datasource as noted above. You will need to specify any module dependencies. You will also need to determine if the application has any dependencies on archives that do not ship with AS7 and copy any dependent JARs into the application lib/ directory.
Some Seam 2 examples use a default JDBC datasource named java:/ExampleDS. The easiest way is to define this datasource is to add the following datasource definition to the <jbossas7_dir>/standalone/configuration/standalone.xml file the :
<
datasource
name
=
"ExampleDS"
jndi-name
=
"java:/ExampleDS"
enabled
=
"true"
jta
=
"true"
use-java-context
=
"true"
use-ccm
=
"true"
>
<
connection-url
>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</
connection-url
>
<
driver
>h2</
driver
>
<
security
>
<
user-name
>sa</
user-name
>
<
password
>sa</
password
>
</
security
>
</
datasource
>
|
This definition is a copy of the default HSQL datasource defined in JBoss AS 7.
You can also add the datasource using the jboss-cli command line interface:
$ <jboss-as7>/bin/jboss-cli.sh --connect [standalone@localhost:9999 /] data-source add --name=ExampleDS --jndi-name=java:/ExampleDS --connection-url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 --driver-name=h2 --user-name=sa --password=sa
Since Seam 2 applications use JSF 1.2, you will need to add dependencies for the JSF 1.2 modules and exclude the JSF 2.0 modules. You will need to create a jboss-deployment-structure.xml file in the EAR META-INF/ directory that contains the following data:
<
jboss-deployment-structure
xmlns
=
"urn:jboss:deployment-structure:1.0"
>
<
deployment
>
<
dependencies
>
<
module
name
=
"javax.faces.api"
slot
=
"1.2"
export
=
"true"
/>
<
module
name
=
"com.sun.jsf-impl"
slot
=
"1.2"
export
=
"true"
/>
</
dependencies
>
</
deployment
>
<
sub-deployment
name
=
"jboss-seam-booking.war"
>
<
exclusions
>
<
module
name
=
"javax.faces.api"
slot
=
"main"
/>
<
module
name
=
"com.sun.jsf-impl"
slot
=
"main"
/>
</
exclusions
>
<
dependencies
>
<
module
name
=
"javax.faces.api"
slot
=
"1.2"
/>
<
module
name
=
"com.sun.jsf-impl"
slot
=
"1.2"
/>
</
dependencies
>
</
sub-deployment
>
</
jboss-deployment-structure
>
|
If your application uses any third-party logging frameworks you will need add dependencies as described in that section of the migration guide.
Even if your Seam 2 application uses Hibernate 3, you may still be able to run with the Hibernate 4 module packaged in AS7. Some hibernate classes are no longer available and you may need to copy one or more of the older hibernate JARs into the /lib directory. If you end up with ClassNotFoundExceptions or ClassCastExceptions involving hibernate classes, you may have to exclude the hibernate module in the deployments section of the META-INF/jboss-deployment-structure.xml
<
jboss-deployment-structure
xmlns
=
"urn:jboss:deployment-structure:1.0"
>
<
deployment
>
<
exclusions
>
<
module
name
=
"org.hibernate"
/>
</
exclusions
>
<
deployment
>
</
jboss-deployment-structure
>
|
<!
- <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/> ->
<
jboss-deployment-structure
>
<
deployment
>
<
exclusions
>
<
module
name
=
"javax.faces.api"
slot
=
"main"
/>
<
module
name
=
"com.sun.jsf-impl"
slot
=
"main"
/>
</
exclusions
>
<
dependencies
>
<
module
name
=
"org.apache.log4j"
/>
<
module
name
=
"org.dom4j"
/>
<
module
name
=
"org.apache.commons.logging"
/>
<
module
name
=
"org.apache.commons.collections"
/>
<
module
name
=
"javax.faces.api"
slot
=
"1.2"
/>
<
module
name
=
"com.sun.jsf-impl"
slot
=
"1.2"
/>
</
dependencies
>
</
deployment
>
</
jboss-deployment-structure
>
|
When you migrate a Seam 2 application, you may see javax.naming.NameNotFoundException errors in the log like the following:
javax.naming.NameNotFoundException: Name 'jboss-seam-booking' not found in context ''
If you don't want to modify JNDI lookups throughout the code, you can modify the application's components.xml file.
<!-- <core:init jndi-pattern="jboss-seam-booking/#{ejbName}/local" debug="true" distributable="false"/> -->
<
core:init
debug
=
"true"
distributable
=
"false"
/>
|
<
component
class
=
"org.jboss.seam.example.booking.AuthenticatorAction"
jndi-name
=
"java:app/jboss-seam-booking.jar/AuthenticatorAction"
/>
|
ToDo: Complete this section
For information on how to migrate Spring applications, see details at the Spring applications development and migration guide.
Due to the modular class loading changes, you might run into ClassNotFoundExceptions or ClassCastExceptions when you migrate your application. To resolve these dependencies, you will need to find the JARs that contain the classes specified by the exceptions.
Tattletale is an excellent tool that recursively scans your application and provides detailed reports about its contents. Tattletale 1.2.0.Beta2 or later contains additional support to help with the new JBoss Modules class loading used in AS7. Tattletale's "JBoss AS7" report can be used to to automatically identify and specify dependent module names in your application's jboss-deployment-structure.xml file.
profiles=java5, java6, ee6, jbossas7
|
java -jar TATTLETALE_HOME/tattletale.jar APPLICATION_ARCHIVE OUTPUT_DIRECTORY
|
For example:
java -jar ~/tattletale-
1.2
.
0
.Beta1/tattletale.jar applications/jboss-seam-booking.ear output-results/
|
Tattletale will only find dependencies on the application classes. It will not find dependencies that may be required by classes your application calls or by classes in other JARs included under your application's WEB-INF/lib directory. To identify external dependencies, you will need to look at the "Depends On" report. |
In previous versions of the application server, datasources and resource adapters were configured and deployed using a file with a suffix of *-ds.xml. The IronJacamar 1.1 distribution contains a migration tool that can be used to convert the datasource and resource adapter configuration files from previous releases into the configuration format expected by the AS7 server. The tool parses the source configuration file from the previous release, then creates and writes an XML snippet in the appropriate format that can be copied and pasted under the correct subsystem in the server configuration file.
The tool will do a best effort to convert all old attributes and elements to the new format. It will be necessary to make additional changes to the generated file. In the following examples, we will need to make a few changes to the resulting XML. Please, consult this documentation for additional information. |
./converter.sh -ds FULLY_QUALIFIED_SOURCE_FILE_NAME FULLY_QUALIFIED_TARGET_FILE_NAME
|
For Windows, type:
converter -ds FULLY_QUALIFIED_SOURCE_FILE_NAME FULLY_QUALIFIED_TARGET_FILE_NAME
|
The tool generates the datasources and datasource element. If you have existing datasources, you need only copy the datasource element into the configuration file. |
Here is an example of the datasource configuration file for the Seam Booking example that shipped with EAP 5.x:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
datasources
>
<
local-tx-datasource
>
<
jndi-name
>bookingDatasource</
jndi-name
>
<
connection-url
>jdbc:hsqldb:.</
connection-url
>
<
driver-class
>org.hsqldb.jdbcDriver</
driver-class
>
<
user-name
>sa</
user-name
>
<
password
></
password
>
</
local-tx-datasource
>
</
datasources
>
|
The generated file will contain a driver-class element. The preferred way to define the driver class is using a drivers element. Here is the converted XML with the driver-class and drivers modifications as configured in the AS7 configuration file:
<
subsystem
xmlns
=
"urn:jboss:domain:datasources:1.0"
>
<
datasources
>
<
datasource
enabled
=
"true"
jndi-name
=
"java:jboss/datasources/bookingDatasource"
jta
=
"true"
pool-name
=
"bookingDatasource"
use-ccm
=
"true"
use-java-context
=
"true"
>
<
connection-url
>jdbc:hsqldb:.</
connection-url
>
<!-- We commented out the following driver-class element since it is not the preferred way to define this.
-<driver-class>org.hsqldb.jdbcDriver</driver-class>- -->
<
transaction-isolation
>TRANSACTION_NONE</
transaction-isolation
>
<
pool
>
<
prefill
>false</
prefill
>
<
use-strict-min
>false</
use-strict-min
>
<
flush-strategy
>FailingConnectionOnly</
flush-strategy
>
</
pool
>
<
security
>
<
user-name
>sa</
user-name
>
<
password
/>
</
security
>
<
validation
>
<
validate-on-match
>false</
validate-on-match
>
<
background-validation
>false</
background-validation
>
<
use-fast-fail
>false</
use-fast-fail
>
</
validation
>
<
timeout
/>
<
statement
>
<
track-statements
>false</
track-statements
>
</
statement
>
</
datasource
>
<!-- The following drivers element was not in the generated XML snippet. We added this manually. -->
<
drivers
>
<
driver
name
=
"h2"
module
=
"com.h2database.h2"
>
<
xa-datasource-class
>org.h2.jdbcx.JdbcDataSource</
xa-datasource-class
>
</
driver
>
</
drivers
>
</
datasources
>
</
subsystem
>
|
./converter.sh -ra FULLY_QUALIFIED_SOURCE_FILE_NAME FULLY_QUALIFIED_TARGET_FILE_NAME
|
For Windows, type:
./converter.sh -ra FULLY_QUALIFIED_SOURCE_FILE_NAME FULLY_QUALIFIED_TARGET_FILE_NAME
|
The tool generates the resource-adapters and resource-adapter element. If you have existing resource-adapters, you need to copy only theresource-adapter element into the configuration file. |
Here is an example of the mttestadapter-ds.xml resource-adapter configuration file from the EAP-5.x TestSuite.:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!-- ==================================================================== -->
<!-- ConnectionManager setup for jboss test adapter -->
<!-- Build jmx-api (build/build.sh all) and view for config documentation -->
<!-- ==================================================================== -->
<
connection-factories
>
<
tx-connection-factory
>
<
jndi-name
>JBossTestCF</
jndi-name
>
<
xa-transaction
/>
<
rar-name
>jbosstestadapter.rar</
rar-name
>
<
connection-definition
>javax.resource.cci.ConnectionFactory</
connection-definition
>
<
config-property
name
=
"IntegerProperty"
type
=
"java.lang.Integer"
>2</
config-property
>
<
config-property
name
=
"BooleanProperty"
type
=
"java.lang.Boolean"
>false</
config-property
>
<
config-property
name
=
"DoubleProperty"
type
=
"java.lang.Double"
>5.5</
config-property
>
<
config-property
name
=
"UrlProperty"
type
=
"java.net.URL"
></ config-property
>
<
config-property
name
=
"sleepInStart"
type
=
"long"
>200</
config-property
>
<
config-property
name
=
"sleepInStop"
type
=
"long"
>200</
config-property
>
</
tx-connection-factory
>
<
tx-connection-factory
>
<
jndi-name
>JBossTestCF2</
jndi-name
>
<
xa-transaction
/>
<
rar-name
>jbosstestadapter.rar</
rar-name
>
<
connection-definition
>javax.resource.cci.ConnectionFactory</
connection-definition
>
<
config-property
name
=
"IntegerProperty"
type
=
"java.lang.Integer"
>2</
config-property
>
<
config-property
name
=
"BooleanProperty"
type
=
"java.lang.Boolean"
>false</
config-property
>
<
config-property
name
=
"DoubleProperty"
type
=
"java.lang.Double"
>5.5</
config-property
>
<
config-property
name
=
"UrlProperty"
type
=
"java.net.URL"
></ config-property
>
<
config-property
name
=
"sleepInStart"
type
=
"long"
>200</
config-property
>
<
config-property
name
=
"sleepInStop"
type
=
"long"
>200</
config-property
>
</
tx-connection-factory
>
<
tx-connection-factory
>
<
jndi-name
>JBossTestCFByTx</
jndi-name
>
<
xa-transaction
/>
<
track-connection-by-tx
>true</
track-connection-by-tx
>
<
rar-name
>jbosstestadapter.rar</
rar-name
>
<
connection-definition
>javax.resource.cci.ConnectionFactory</
connection-definition
>
<
config-property
name
=
"IntegerProperty"
type
=
"java.lang.Integer"
>2</
config-property
>
<
config-property
name
=
"BooleanProperty"
type
=
"java.lang.Boolean"
>false</
config-property
>
<
config-property
name
=
"DoubleProperty"
type
=
"java.lang.Double"
>5.5</
config-property
>
<
config-property
name
=
"UrlProperty"
type
=
"java.net.URL"
></ config-property
>
<
config-property
name
=
"sleepInStart"
type
=
"long"
>200</
config-property
>
<
config-property
name
=
"sleepInStop"
type
=
"long"
>200</
config-property
>
</
tx-connection-factory
>
</
connection-factories
>
|
You will need to replace the class-name attribute "FIXME_MCF_CLASS_NAME" in the generated XML snippet with the class name of the managed connection factory, in this case, "org.jboss.test.jca.adapter.TestManagedConnectionFactory". Here is the server configuration file with the newly generated and edited configuration data:
<
subsystem
xmlns
=
"urn:jboss:domain:resource-adapters:1.0"
>
<
resource-adapters
>
<
resource-adapter
>
<
archive
>jbosstestadapter.rar</
archive
>
<
transaction-support
>XATransaction</
transaction-support
>
<
connection-definitions
>
<!-- We replaced the following "FIXME_MCF_CLASS_NAME" class-name value with the correct class name
<connection-definition class-name="FIXME_MCF_CLASS_NAME" enabled="true"
jndi-name="java:jboss/JBossTestCF" pool-name="JBossTestCF" use-ccm="true" use-java-context="true"> -->
<
connection-definition
class-name
=
"org.jboss.test.jca.adapter.TestManagedConnectionFactory"
enabled
=
"true"
jndi-name
=
"java:jboss/JBossTestCF"
pool-name
=
"JBossTestCF"
use-ccm
=
"true"
use-java-context
=
"true"
>
<
config-property
name
=
"IntegerProperty"
>2</
config-property
>
<
config-property
name
=
"sleepInStart"
>200</
config-property
>
<
config-property
name
=
"sleepInStop"
>200</
config-property
>
<
config-property
name
=
"BooleanProperty"
>false</
config-property
>
<
config-property
name
=
"UrlProperty"
></ config-property
>
<
config-property
name
=
"DoubleProperty"
>5.5</
config-property
>
<
pool
>
<
prefill
>false</
prefill
>
<
use-strict-min
>false</
use-strict-min
>
<
flush-strategy
>FailingConnectionOnly</
flush-strategy
>
</
pool
>
<
security
>
<
application
/>
</
security
>
<
timeout
/>
<
validation
>
<
background-validation
>false</
background-validation
>
<
use-fast-fail
>false</
use-fast-fail
>
</
validation
>
</
connection-definition
>
</
connection-definitions
>
</
resource-adapter
>
<
resource-adapter
>
<
archive
>jbosstestadapter.rar</
archive
>
<
transaction-support
>XATransaction</
transaction-support
>
<
connection-definitions
>
<!-- We replaced the following "FIXME_MCF_CLASS_NAME" class-name value with the correct class name
<connection-definition class-name="FIXME_MCF_CLASS_NAME" enabled="true"
jndi-name="java:jboss/JBossTestCF2" pool-name="JBossTestCF2" use-ccm="true" use-java-context="true"> -->
<
connection-definition
class-name
=
"org.jboss.test.jca.adapter.TestManagedConnectionFactory"
enabled
=
"true"
jndi-name
=
"java:jboss/JBossTestCF2"
pool-name
=
"JBossTestCF2"
use-ccm
=
"true"
use-java-context
=
"true"
>
<
config-property
name
=
"IntegerProperty"
>2</
config-property
>
<
config-property
name
=
"sleepInStart"
>200</
config-property
>
<
config-property
name
=
"sleepInStop"
>200</
config-property
>
<
config-property
name
=
"BooleanProperty"
>false</
config-property
>
<
config-property
name
=
"UrlProperty"
></ config-property
>
<
config-property
name
=
"DoubleProperty"
>5.5</
config-property
>
<
pool
>
<
prefill
>false</
prefill
>
<
use-strict-min
>false</
use-strict-min
>
<
flush-strategy
>FailingConnectionOnly</
flush-strategy
>
</
pool
>
<
security
>
<
application
/>
</
security
>
<
timeout
/>
<
validation
>
<
background-validation
>false</
background-validation
>
<
use-fast-fail
>false</
use-fast-fail
>
</
validation
>
</
connection-definition
>
</
connection-definitions
>
</
resource-adapter
>
<
resource-adapter
>
<
archive
>jbosstestadapter.rar</
archive
>
<
transaction-support
>XATransaction</
transaction-support
>
<
connection-definitions
>
<!-- We replaced the following "FIXME_MCF_CLASS_NAME" class-name value with the correct class name
<connection-definition class-name="FIXME_MCF_CLASS_NAME" enabled="true"
jndi-name="java:jboss/JBossTestCFByTx" pool-name="JBossTestCFByTx" use-ccm="true" use-java-context="true"> -->
<
connection-definition
class-name
=
"org.jboss.test.jca.adapter.TestManagedConnectionFactory"
enabled
=
"true"
jndi-name
=
"java:jboss/JBossTestCFByTx"
pool-name
=
"JBossTestCFByTx"
use-ccm
=
"true"
use-java-context
=
"true"
>
<
config-property
name
=
"IntegerProperty"
>2</
config-property
>
<
config-property
name
=
"sleepInStart"
>200</
config-property
>
<
config-property
name
=
"sleepInStop"
>200</
config-property
>
<
config-property
name
=
"BooleanProperty"
>false</
config-property
>
<
config-property
name
=
"UrlProperty"
></ config-property
>
<
config-property
name
=
"DoubleProperty"
>5.5</
config-property
>
<
pool
>
<
prefill
>false</
prefill
>
<
use-strict-min
>false</
use-strict-min
>
<
flush-strategy
>FailingConnectionOnly</
flush-strategy
>
</
pool
>
<
security
>
<
application
/>
</
security
>
<
timeout
/>
<
validation
>
<
background-validation
>false</
background-validation
>
<
use-fast-fail
>false</
use-fast-fail
>
</
validation
>
</
connection-definition
>
</
connection-definitions
>
</
resource-adapter
>
</
resource-adapters
></
subsystem
>
|
For more information about this migration tool, go to IronJacamar Migration Tool .
This chapter explains how to invoke EJBs from a remote client by using the JNDI API to first lookup the bean proxy and then invoke on that proxy.
After you have read this article, do remember to take a look at Remote EJB invocations via JNDI - EJB client API or remote-naming project |
Before getting into the details, we would like the users to know that we have introduced a new EJB client API, which is a JBoss specific API and allows invocation on remote EJBs. This client API isn't based on JNDI. So remote clients need not rely on JNDI API to invoke on EJBs. A separate document covering the EJB remote client API will be made available. For now, you can refer to the javadocs of the EJB client project at http://docs.jboss.org/ejbclient/. In this document, we'll just concentrate on the traditional JNDI based invocation on EJBs. So let's get started:
Users who already have EJBs deployed on the server side can just skip to the next section. |
As a first step, you'll have to deploy your application containing the EJBs on the AS7 server. If you want those EJBs to be remotely invocable, then you'll have to expose atleast one remote view for that bean. In this example, let's consider a simple Calculator stateless bean which exposes a RemoteCalculator remote business interface. We'll also have a simple stateful CounterBean which exposes a RemoteCounter remote business interface. Here's the code:
package
org.jboss.as.quickstarts.ejb.remote.stateless;
/**
* @author Jaikiran Pai
*/
public
interface
RemoteCalculator {
int
add(
int
a,
int
b);
int
subtract(
int
a,
int
b);
}
|
package
org.jboss.as.quickstarts.ejb.remote.stateless;
import
javax.ejb.Remote;
import
javax.ejb.Stateless;
/**
* @author Jaikiran Pai
*/
@Stateless
@Remote
(RemoteCalculator.
class
)
public
class
CalculatorBean
implements
RemoteCalculator {
@Override
public
int
add(
int
a,
int
b) {
return
a + b;
}
@Override
public
int
subtract(
int
a,
int
b) {
return
a - b;
}
}
|
package
org.jboss.as.quickstarts.ejb.remote.stateful;
/**
* @author Jaikiran Pai
*/
public
interface
RemoteCounter {
void
increment();
void
decrement();
int
getCount();
}
|
package
org.jboss.as.quickstarts.ejb.remote.stateful;
import
javax.ejb.Remote;
import
javax.ejb.Stateful;
/**
* @author Jaikiran Pai
*/
@Stateful
@Remote
(RemoteCounter.
class
)
public
class
CounterBean
implements
RemoteCounter {
private
int
count =
0
;
@Override
public
void
increment() {
this
.count++;
}
@Override
public
void
decrement() {
this
.count--;
}
@Override
public
int
getCount() {
return
this
.count;
}
}
|
Let's package this in a jar (how you package it in a jar is out of scope of this chapter) named "jboss-as-ejb-remote-app.jar" and deploy it to the server. Make sure that your deployment has been processed successfully and there aren't any errors.
The next step is to write an application which will invoke the EJBs that you deployed on the server. In AS7, you can either choose to use the JBoss specific EJB client API to do the invocation or use JNDI to lookup a proxy for your bean and invoke on that returned proxy. In this chapter we will concentrate on the JNDI lookup and invocation and will leave the EJB client API for a separate chapter.
So let's take a look at what the client code looks like for looking up the JNDI proxy and invoking on it. Here's the entire client code which invokes on a stateless bean:
package
org.jboss.as.quickstarts.ejb.remote.client;
import
javax.naming.Context;
import
javax.naming.InitialContext;
import
javax.naming.NamingException;
import
java.security.Security;
import
java.util.Hashtable;
import
org.jboss.as.quickstarts.ejb.remote.stateful.CounterBean;
import
org.jboss.as.quickstarts.ejb.remote.stateful.RemoteCounter;
import
org.jboss.as.quickstarts.ejb.remote.stateless.CalculatorBean;
import
org.jboss.as.quickstarts.ejb.remote.stateless.RemoteCalculator;
import
org.jboss.sasl.JBossSaslProvider;
/**
* A sample program which acts a remote client for a EJB deployed on AS7 server.
* This program shows how to lookup stateful and stateless beans via JNDI and then invoke on them
*
* @author Jaikiran Pai
*/
public
class
RemoteEJBClient {
public
static
void
main(String[] args)
throws
Exception {
// Invoke a stateless bean
invokeStatelessBean();
// Invoke a stateful bean
invokeStatefulBean();
}
/**
* Looks up a stateless bean and invokes on it
*
* @throws NamingException
*/
private
static
void
invokeStatelessBean()
throws
NamingException {
// Let's lookup the remote stateless calculator
final
RemoteCalculator statelessRemoteCalculator = lookupRemoteStatelessCalculator();
System.out.println(
"Obtained a remote stateless calculator for invocation"
);
// invoke on the remote calculator
int
a =
204
;
int
b =
340
;
System.out.println(
"Adding "
+ a +
" and "
+ b +
" via the remote stateless calculator deployed on the server"
);
int
sum = statelessRemoteCalculator.add(a, b);
System.out.println(
"Remote calculator returned sum = "
+ sum);
if
(sum != a + b) {
throw
new
RuntimeException(
"Remote stateless calculator returned an incorrect sum "
+ sum +
" ,expected sum was "
+ (a + b));
}
// try one more invocation, this time for subtraction
int
num1 =
3434
;
int
num2 =
2332
;
System.out.println(
"Subtracting "
+ num2 +
" from "
+ num1 +
" via the remote stateless calculator deployed on the server"
);
int
difference = statelessRemoteCalculator.subtract(num1, num2);
System.out.println(
"Remote calculator returned difference = "
+ difference);
if
(difference != num1 - num2) {
throw
new
RuntimeException(
"Remote stateless calculator returned an incorrect difference "
+ difference +
" ,expected difference was "
+ (num1 - num2));
}
}
/**
* Looks up a stateful bean and invokes on it
*
* @throws NamingException
*/
private
static
void
invokeStatefulBean()
throws
NamingException {
// Let's lookup the remote stateful counter
final
RemoteCounter statefulRemoteCounter = lookupRemoteStatefulCounter();
System.out.println(
"Obtained a remote stateful counter for invocation"
);
// invoke on the remote counter bean
final
int
NUM_TIMES =
20
;
System.out.println(
"Counter will now be incremented "
+ NUM_TIMES +
" times"
);
for
(
int
i =
0
; i < NUM_TIMES; i++) {
System.out.println(
"Incrementing counter"
);
statefulRemoteCounter.increment();
System.out.println(
"Count after increment is "
+ statefulRemoteCounter.getCount());
}
// now decrementing
System.out.println(
"Counter will now be decremented "
+ NUM_TIMES +
" times"
);
for
(
int
i = NUM_TIMES; i >
0
; i--) {
System.out.println(
"Decrementing counter"
);
statefulRemoteCounter.decrement();
System.out.println(
"Count after decrement is "
+ statefulRemoteCounter.getCount());
}
}
/**
* Looks up and returns the proxy to remote stateless calculator bean
*
* @return
* @throws NamingException
*/
private
static
RemoteCalculator lookupRemoteStatelessCalculator()
throws
NamingException {
final
Hashtable jndiProperties =
new
Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES,
"org.jboss.ejb.client.naming"
);
final
Context context =
new
InitialContext(jndiProperties);
// The app name is the application name of the deployed EJBs. This is typically the ear name
// without the .ear suffix. However, the application name could be overridden in the application.xml of the
// EJB deployment on the server.
// Since we haven't deployed the application as a .ear, the app name for us will be an empty string
final
String appName =
""
;
// This is the module name of the deployed EJBs on the server. This is typically the jar name of the
// EJB deployment, without the .jar suffix, but can be overridden via the ejb-jar.xml
// In this example, we have deployed the EJBs in a jboss-as-ejb-remote-app.jar, so the module name is
// jboss-as-ejb-remote-app
final
String moduleName =
"jboss-as-ejb-remote-app"
;
// AS7 allows each deployment to have an (optional) distinct name. We haven't specified a distinct name for
// our EJB deployment, so this is an empty string
final
String distinctName =
""
;
// The EJB name which by default is the simple class name of the bean implementation class
final
String beanName = CalculatorBean.
class
.getSimpleName();
// the remote view fully qualified class name
final
String viewClassName = RemoteCalculator.
class
.getName();
// let's do the lookup
return
(RemoteCalculator) context.lookup(
"ejb:"
+ appName +
"/"
+ moduleName +
"/"
+ distinctName +
"/"
+ beanName +
"!"
+ viewClassName);
}
/**
* Looks up and returns the proxy to remote stateful counter bean
*
* @return
* @throws NamingException
*/
private
static
RemoteCounter lookupRemoteStatefulCounter()
throws
NamingException {
final
Hashtable jndiProperties =
new
Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES,
"org.jboss.ejb.client.naming"
);
final
Context context =
new
InitialContext(jndiProperties);
// The app name is the application name of the deployed EJBs. This is typically the ear name
// without the .ear suffix. However, the application name could be overridden in the application.xml of the
// EJB deployment on the server.
// Since we haven't deployed the application as a .ear, the app name for us will be an empty string
final
String appName =
""
;
// This is the module name of the deployed EJBs on the server. This is typically the jar name of the
// EJB deployment, without the .jar suffix, but can be overridden via the ejb-jar.xml
// In this example, we have deployed the EJBs in a jboss-as-ejb-remote-app.jar, so the module name is
// jboss-as-ejb-remote-app
final
String moduleName =
"jboss-as-ejb-remote-app"
;
// AS7 allows each deployment to have an (optional) distinct name. We haven't specified a distinct name for
// our EJB deployment, so this is an empty string
final
String distinctName =
""
;
// The EJB name which by default is the simple class name of the bean implementation class
final
String beanName = CounterBean.
class
.getSimpleName();
// the remote view fully qualified class name
final
String viewClassName = RemoteCounter.
class
.getName();
// let's do the lookup (notice the ?stateful string as the last part of the jndi name for stateful bean lookup)
return
(RemoteCounter) context.lookup(
"ejb:"
+ appName +
"/"
+ moduleName +
"/"
+ distinctName +
"/"
+ beanName +
"!"
+ viewClassName +
"?stateful"
);
}
}
|
The entire server side and client side code is hosted at the github repo here https://github.com/jboss-jdf/jboss-as-quickstart/tree/master/ejb-remote |
The code has some comments which will help you understand each of those lines. But we'll explain here in more detail what the code does. As a first step in the client code, we'll do a lookup of the EJB using a JNDI name. In AS7, for remote access to EJBs, you use the ejb: namespace with the following syntax:
For stateless beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-
interface
>
|
For stateful beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-
interface
>?stateful
|
The ejb: namespace identifies it as a EJB lookup and is a constant (i.e. doesn't change) for doing EJB lookups. The rest of the parts in the jndi name are as follows:
app-name : This is the name of the .ear (without the .ear suffix) that you have deployed on the server and contains your EJBs.
module-name : This is the name of the .jar (without the .jar suffix) that you have deployed on the server and the contains your EJBs. If the EJBs are deployed in a .war then the module name is the .war name (without the .war suffix).
distinct-name : This is a JBoss AS7 specific name which can be optionally assigned to the deployments that are deployed on the server. More about the purpose and usage of this will be explained in a separate chapter. If a deployment doesn't use distinct-name then, use an empty string in the JNDI name, for distinct-name
bean-name : This is the name of the bean for which you are doing the lookup. The bean name is typically the unqualified classname of the bean implementation class, but can be overriden through either ejb-jar.xml or via annotations. The bean name part cannot be an empty string in the JNDI name.
fully-qualified-classname-of-the-remote-interface : This is the fully qualified class name of the interface for which you are doing the lookup. The interface should be one of the remote interfaces exposed by the bean on the server. The fully qualified class name part cannot be an empty string in the JNDI name.
For stateful beans, the JNDI name expects an additional "?stateful" to be appended after the fully qualified interface name part. This is because for stateful beans, a new session gets created on JNDI lookup and the EJB client API implementation doesn't contact the server during the JNDI lookup to know what kind of a bean the JNDI name represents (we'll come to this in a while). So the JNDI name itself is expected to indicate that the client is looking up a stateful bean, so that an appropriate session can be created.
Now that we know the syntax, let's see our code and check what JNDI name it uses. Since our stateless EJB named CalculatorBean is deployed in a jboss-as-ejb-remote-app.jar (without any ear) and since we are looking up the org.jboss.as.quickstarts.ejb.remote.stateless.RemoteCalculator remote interface, our JNDI name will be:
ejb:/jboss-as-ejb-remote-app
//CalculatorBean!org.jboss.as.quickstarts.ejb.remote.stateless.RemoteCalculator
|
That's what the lookupRemoteStatelessCalculator() method in the above client code uses.
For the stateful EJB named CounterBean which is deployed in hte same jboss-as-ejb-remote-app.jar and which exposes the org.jboss.as.quickstarts.ejb.remote.stateful.RemoteCounter, the JNDI name will be:
ejb:/jboss-as-ejb-remote-app
//CounterBean!org.jboss.as.quickstarts.ejb.remote.stateful.RemoteCounter?stateful
|
That's what the lookupRemoteStatefulCounter() method in the above client code uses.
Now that we know of the JNDI name, let's take a look at the following piece of code in the lookupRemoteStatelessCalculator():
final
Hashtable jndiProperties =
new
Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES,
"org.jboss.ejb.client.naming"
);
final
Context context =
new
InitialContext(jndiProperties);
|
Here we are creating a JNDI InitialContext object by passing it some JNDI properties. The Context.URL_PKG_PREFIXES is set to org.jboss.ejb.client.naming. This is necessary because we should let the JNDI API know what handles the ejb: namespace that we use in our JNDI names for lookup. The "org.jboss.ejb.client.naming" has a URLContextFactory implementation which will be used by the JNDI APIs to parse and return an object for ejb: namespace lookups. You can either pass these properties to the constructor of the InitialContext class or have a jndi.properites file in the classpath of the client application, which (atleast) contains the following property:
java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
|
So at this point, we have setup the InitialContext and also have the JNDI name ready to do the lookup. You can now do the lookup and the appropriate proxy which will be castable to the remote interface that you used as the fully qualified class name in the JNDI name, will be returned. Some of you might be wondering, how the JNDI implementation knew which server address to look, for your deployed EJBs. The answer is in AS7, the proxies returned via JNDI name lookup for ejb: namespace do not connect to the server unless an invocation on those proxies is done.
Now let's get to the point where we invoke on this returned proxy:
// Let's lookup the remote stateless calculator
final
RemoteCalculator statelessRemoteCalculator = lookupRemoteStatelessCalculator();
System.out.println(
"Obtained a remote stateless calculator for invocation"
);
// invoke on the remote calculator
int
a =
204
;
int
b =
340
;
System.out.println(
"Adding "
+ a +
" and "
+ b +
" via the remote stateless calculator deployed on the server"
);
int
sum = statelessRemoteCalculator.add(a, b);
|
We can see here that the proxy returned after the lookup is used to invoke the add(...) method of the bean. It's at this point that the JNDI implementation (which is backed by the JBoss EJB client API) needs to know the server details. So let's now get to the important part of setting up the EJB client context properties.
A EJB client context is a context which contains contextual information for carrying out remote invocations on EJBs. This is a JBoss specific API. The EJB client context can be associated with multiple EJB receivers. Each EJB receiver is capable of handling invocations on different EJBs. For example, an EJB receiver "Foo" might be able to handle invocation on a bean identified by app-A/module-A/distinctinctName-A/Bar!RemoteBar, whereas a EJB receiver named "Blah" might be able to handle invocation on a bean identified by app-B/module-B/distinctName-B/BeanB!RemoteBean. Each such EJB receiver knows about what set of EJBs it can handle and each of the EJB receiver knows which server target to use for handling the invocations on the bean. For example, if you have a AS7 server at 10.20.30.40 IP address which has its remoting port opened at 4447 and if that's the server on which you deployed that CalculatorBean, then you can setup a EJB receiver which knows its target address is 10.20.30.40:4447. Such an EJB receiver will be capable enough to communicate to the server via the JBoss specific EJB remote client protocol (details of which will be explained in-depth in a separate chapter).
Now that we know what a EJB client context is and what a EJB receiver is, let's see how we can setup a client context with 1 EJB receiver which can connect to 10.20.30.40 IP address at port 4447. That EJB client context will then be used (internally) by the JNDI implementation to handle invocations on the bean proxy.
The client will have to place a jboss-ejb-client.properties file in the classpath of the application. The jboss-ejb-client.properties can contain the following properties:
endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=
false
remote.connections=
default
remote.connection.
default
.host=
10.20
.
30.40
remote.connection.
default
.port =
4447
remote.connection.
default
.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=
false
remote.connection.
default
.username=appuser
remote.connection.
default
.password=apppassword
|
The above properties file is just an example. The actual file that was used for this sample program is available here for reference https://github.com/jboss-jdf/jboss-as-quickstart/blob/master/ejb-remote/client/src/main/resources/jboss-ejb-client.properties |
We'll see what each of it means.
First the endpoint.name property. We mentioned earlier that the EJB receivers will communicate with the server for EJB invocations. Internally, they use JBoss Remoting project to carry out the communication. The endpoint.name property represents the name that will be used to create the client side of the enpdoint. The endpoint.name property is optional and if not specified in the jboss-ejb-client.properties file, it will default to "config-based-ejb-client-endpoint" name.
Next is the remote.connectionprovider.create.options.<....> properties:
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=
false
|
The "remote.connectionprovider.create.options." property prefix can be used to pass the options that will be used while create the connection provider which will handle the "remote:" protocol. In this example we use the "remote.connectionprovider.create.options." property prefix to pass the "org.xnio.Options.SSL_ENABLED" property value as false. That property will then be used during the connection provider creation. Similarly other properties can be passed too, just append it to the "remote.connectionprovider.create.options." prefix
Next we'll see:
remote.connections=
default
|
This is where you define the connections that you want to setup for communication with the remote server. The "remote.connections" property uses a comma separated value of connection "names". The connection names are just logical and are used grouping together the connection configuration properties later on in the properties file. The example above sets up a single remote connection named "default". There can be more than one connections that are configured. For example:
remote.connections=one, two
|
Here we are listing 2 connections named "one" and "two". Ultimately, each of the connections will map to a EJB receiver. So if you have 2 connections, that will setup 2 EJB receivers that will be added to the EJB client context. Each of these connections will be configured with the connection specific properties as follows:
remote.connection.
default
.host=
10.20
.
30.40
remote.connection.
default
.port =
4447
remote.connection.
default
.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=
false
|
As you can see we are using the "remote.connection.<connection-name>." prefix for specifying the connection specific property. The connection name here is "default" and we are setting the "host" property of that connection to point to 10.20.30.40. Similarly we set the "port" for that connection to 4447.
By default AS7 uses 4447 as the remoting port. The EJB client API uses the remoting port for communicating with the server for remote invocations, so that's the port we use in our client programs (unless the server is configured for some other remoting port) |
remote.connection.
default
.username=appuser
remote.connection.
default
.password=apppassword
|
The given user/password must be set by using the command bin/add-user.sh (or.bat).
The user and password must be set because the security-realm is enabled for the subsystem remoting (see standalone*.xml or domain.xml) by default.
If you do not need the security for remoting you might remove the attribute security-realm in the configuration.
security-realm is possible since 7.1.0.FINAL and enabled by default. |
We then use the "remote.connection.<connection-name>.connect.options." property prefix to setup options that will be used during the connection creation.
Here's an example of setting up multiple connections with different properties for each of those:
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=
false
remote.connections=one, two
remote.connection.one.host=localhost
remote.connection.one.port=
6999
remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=
false
remote.connection.two.host=localhost
remote.connection.two.port=
7999
remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=
false
|
As you can see we setup 2 connections "one" and "two" which both point to "localhost" as the "host" but different ports. Each of these connections will internally be used to create the EJB receivers in the EJB client context.
So that's how the jboss-ejb-client.properties file can be setup and placed in the classpath.
The EJB client code will by default look for jboss-ejb-client.properties in the classpath. However, you can specify a different file of your choice by setting the "jboss.ejb.client.properties.file.path" system property which points to a properties file on your filesystem, containing the client context configurations. An example for that would be "-Djboss.ejb.client.properties.file.path=/home/me/my-client/custom-jboss-ejb-client.properties"
Starting JBoss AS 7.1.0.Final, a jboss-client jar is shipped in the distribution. It's available at JBOSS_HOME/bin/client/jboss-client-7.1.0.Final.jar. Place this jar in the classpath of your client application.
If you are using Maven to build the client application, then please follow the instructions in the JBOSS_HOME/bin/client/README.txt to add this jar as a Maven dependency. |
In the above examples, we saw what it takes to invoke a EJB from a remote client. To summarize:
The purpose of this chapter is to demonstrate how to lookup and invoke on EJBs deployed on an AS7 server instance from another AS7 server instance. This is different from invoking the EJBs from a remote standalone client
Let's call the server, from which the invocation happens to the EJB, as "Client Server" and the server on which the bean is deployed as the "Destination Server".
Note that this chapter deals with the case where the bean is deployed on the "Destination Server" but not on the "Client Server". |
In this example, we'll consider a EJB which is packaged in a myejb.jar which is within a myapp.ear. Here's how it would look like:
myapp.ear
|
|---- myejb.jar
| |
| |---- <org.myapp.ejb.*>
// EJB classes
|
Note that packaging itself isn't really important in the context of this article. You can deploy the EJBs in any standard way (.ear, .war or .jar). |
In our example, we'll consider a simple stateless session bean which is as follows:
package
org.myapp.ejb;
public
interface
Greeter {
String greet(String user);
}
|
package
org.myapp.ejb;
import
javax.ejb.Remote;
import
javax.ejb.Stateless;
@Stateless
@Remote
(Greeter.
class
)
public
class
GreeterBean
implements
Greeter {
@Override
public
String greet(String user) {
return
"Hello "
+ user +
", have a pleasant day!"
;
}
}
|
JBoss AS 7.1 is secure by default. What this means is that no communication can happen with an AS7 instance from a remote client (irrespective of whether it is a standalone client or another server instance) without passing the appropriate credentials. Remember that in this example, our "client server" will be communicating with the "destination server". So in order to allow this communication to happen successfully, we'll have to configure user credentials which we will be using during this communication. So let's start with the necessary configurations for this.
As a first step we'll configure a user on the destination server who will be allowed to access the destination server. We create the user using the add-user script that's available in the JBOSS_HOME/bin folder. In this example, we'll be configuring a Application User named ejb and with a password test in the ApplicationRealm. Running the add-user script is an interactive process and you will see questions/output as follows:
jpai
@jpai
-laptop:bin$ ./add-user.sh
What type of user
do
you wish to add?
a) Management User (mgmt-users.properties)
b) Application User (application-users.properties)
(a): b
Enter the details of the
new
user to add.
Realm (ApplicationRealm) :
Username : ejb
Password :
Re-enter Password :
What roles
do
you want
this
user to belong to? (Please enter a comma separated list, or leave blank
for
none)\[ \]:
About to add user
'ejb'
for
realm
'ApplicationRealm'
Is
this
correct yes/no? yes
Added user
'ejb'
to file
'/jboss-as-7.1.1.Final/standalone/configuration/application-users.properties'
Added user
'ejb'
to file
'/jboss-as-7.1.1.Final/domain/configuration/application-users.properties'
Added user
'ejb'
with roles to file
'/jboss-as-7.1.1.Final/standalone/configuration/application-roles.properties'
Added user
'ejb'
with roles to file
'/jboss-as-7.1.1.Final/domain/configuration/application-roles.properties'
|
As you can see in the output above we have now configured a user on the destination server who'll be allowed to access this server. We'll use this user credentials later on in the client server for communicating with this server. The important bits to remember are the user we have created in this example is ejb and the password is test.
Note that you can use any username and password combination you want to. |
You do not require the server to be started to add a user using the add-user script. |
As a next step towards running this example, we'll start the "Destination Server". In this example, we'll use the standalone server and use the standalone-full.xmlconfiguration. The startup command will look like:
.
/standalone
.sh -server-config=standalone-full.xml
|
Ensure that the server has started without any errors.
It's very important to note that if you are starting both the server instances on the same machine, then each of those server instances must have a uniquejboss.node.name system property. You can do that by passing an appropriate value for -Djboss.node.name system property to the startup script:
|
The application (myapp.ear in our case) will be deployed to "Destination Server". The process of deploying the application is out of scope of this chapter. You can either use the Command Line Interface or the Admin console or any IDE or manually copy it to JBOSS_HOME/standalone/deployments folder (for standalone server). Just ensure that the application has been deployed successfully.
So far, we have built a EJB application and deployed it on the "Destination Server". Now let's move to the "Client Server" which acts as the client for the deployed EJBs on the "Destination Server".
As a first step on the "Client Server", we need to let the server know about the "Destination Server"'s EJB remoting connector, over which it can communicate during the EJB invocations. To do that, we'll have to add a "remote-outbound-connection" to the remoting subsystem on the "Client Server". The "remote-outbound-connection" configuration indicates that a outbound connection will be created to a remote server instance from that server. The "remote-outbound-connection" will be backed by a "outbound-socket-binding" which will point to a remote host and a remote port (of the "Destination Server"). So let's see how we create these configurations.
In this example, we'll start the "Client Server" on the same machine as the "Destination Server". We have copied the entire server installation to a different folder and while starting the "Client Server" we'll use a port-offset (of 100 in this example) to avoid port conflicts:
.
/standalone
.sh -server-config=standalone-full.xml -Djboss.socket.binding.port-offset=100
|
Remember that we need to communicate with a secure destination server. In order to do that the client server has to pass the user credentials to the destination server. Earlier we created a user on the destination server who'll be allowed to communicate with that server. Now on the "client server" we'll create a security-realm which will be used to pass the user information.
In this example we'll use a security realm which stores a Base64 encoded password and then passes on that credentials when asked for. Earlier we created a user namedejb and password test. So our first task here would be to create the base64 encoded version of the password test. You can use any utility which generates you a base64 version for a string. I used this online site which generates the base64 encoded string. So for the test password, the base64 encoded version is dGVzdA==
While generating the base64 encoded string make sure that you don't have any trailing or leading spaces for the original password. That can lead to incorrect encoded versions being generated. |
With new versions the add-user script will show the base64 password if you type 'y' if you've been ask
|
Now that we have generated that base64 encoded password, let's use in the in the security realm that we are going to configure on the "client server". I'll first shutdown the client server and edit the standlaone-full.xml file to add the following in the <management> section
Now let's create a "security-realm" for the base64 encoded password.
/core-service
=management
/security-realm
=ejb-security-realm:add()
/core-service
=management
/security-realm
=ejb-security-realm
/server-identity
=secret:add(value=dGVzdA==)
|
Notice that the CLI show the message "process-state" => "reload-required", so you have to restart the server before you can use this change. |
upon successful invocation of this command, the following configuration will be created in the management section:
<
management
>
<
security-realms
>
...
<
security-realm
name
=
"ejb-security-realm"
>
<
server-identities
>
<
secret
value
=
"dGVzdA=="
/>
</
server-identities
>
</
security-realm
>
</
security-realms
>
...
|
As you can see I have created a security realm named "ejb-security-realm" (you can name it anything) with the base64 encoded password. So that completes the security realm configuration for the client server. Now let's move on to the next step.
Let's first create a outbound-socket-binding which points the "Destination Server"'s host and port. We'll use the CLI to create this configuration:
/socket-binding-group
=standard-sockets
/remote-destination-outbound-socket-binding
=remote-ejb:add(host=localhost, port=4447)
|
The above command will create a outbound-socket-binding named "remote-ejb" (we can name it anything) which points to "localhost" as the host and port 4447 as the destination port. Note that the host information should match the host/IP of the "Destination Server" (in this example we are running on the same machine so we use "localhost") and the port information should match the remoting connector port used by the EJB subsystem (by default it's 4447). When this command is run successfully, we'll see that the standalone-full.xml (the file which we used to start the server) was updated with the following outbound-socket-binding in the socket-binding-group:
<
socket-binding-group
name
=
"standard-sockets"
default-interface
=
"public"
port-offset
=
"${jboss.socket.binding.port-offset:0}"
>
...
<
outbound-socket-binding
name
=
"remote-ejb"
>
<
remote-destination
host
=
"localhost"
port
=
"4447"
/>
</
outbound-socket-binding
>
</
socket-binding-group
>
|
Now let's create a "remote-outbound-connection" which will use the newly created outbound-socket-binding (pointing to the EJB remoting connector of the "Destination Server"). We'll continue to use the CLI to create this configuration:
/subsystem
=remoting
/remote-outbound-connection
=remote-ejb-connection:add(outbound-socket-binding-ref=remote-ejb, security-realm=ejb-security-realm, username=ejb)
|
The above command creates a remote-outbound-connection, named "remote-ejb-connection" (we can name it anything), in the remoting subsystem and uses the previously created "remote-ejb" outbound-socket-binding (notice the outbound-socket-binding-ref in that command). Furthermore, we also set the security-realm attribute to point to the security-realm that we created in the previous step. Also notice that we have set the username attribute to use the user name who is allowed to communicate with the destination server.
What this step does is, it creates a outbound connection, on the client server, to the remote destination server and sets up the username to the user who allowed to communicate with that destination server and also sets up the security-realm to a pre-configured security-realm capable of passing along the user credentials (in this case the password). This way when a connection has to be established from the client server to the destination server, the connection creation logic will have the necessary security credentials to pass along and setup a successful secured connection.
Now let's run the following two operations to set some default connection creation options for the outbound connection:
/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SASL_POLICY_NOANONYMOUS:add(value=
false
)
|
/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SSL_ENABLED:add(value=
false
)
|
Ultimately, upon successful invocation of this command, the following configuration will be created in the remoting subsystem:
<
subsystem
xmlns
=
"urn:jboss:domain:remoting:1.1"
>
....
<
outbound-connections
>
<
remote-outbound-connection
name
=
"remote-ejb-connection"
outbound-socket-binding-ref
=
"remote-ejb"
security-realm
=
"ejb-security-realm"
username
=
"ejb"
>
<
properties
>
<
property
name
=
"SASL_POLICY_NOANONYMOUS"
value
=
"false"
/>
<
property
name
=
"SSL_ENABLED"
value
=
"false"
/>
</
properties
>
</
remote-outbound-connection
>
</
outbound-connections
>
</
subsystem
>
|
From a server configuration point of view, that's all we need on the "Client Server". Our next step is to deploy an application on the "Client Server" which will invoke on the bean deployed on the "Destination Server".
Like on the "Destination Server", we'll use .ear packaging for the client application too. But like previously mentioned, that's not mandatory. You can even use a .war or .jar deployments. Here's how our client application packaging will look like:
client-app.ear
|
|--- META-INF
| |
| |--- jboss-ejb-client.xml
|
|--- web.war
| |
| |--- WEB-INF/classes
| | |
| | |---- <org.myapp.FooServlet> // classes in the web app
|
In the client application we'll use a servlet which invokes on the bean deployed on the "Destination Server". We can even invoke the bean on the "Destination Server" from a EJB on the "Client Server". The code remains the same (JNDI lookup, followed by invocation on the proxy). The important part to notice in this client application is the filejboss-ejb-client.xml which is packaged in the META-INF folder of a top level deployment (in this case our client-app.ear). This jboss-ejb-client.xml contains the EJB client configurations which will be used during the EJB invocations for finding the appropriate destinations (also known as, EJB receivers). The contents of the jboss-ejb-client.xml are explained next.
If your application is deployed as a top level .war deployment, then the jboss-ejb-client.xml is expected to be placed in .war/WEB-INF/ folder (i.e. the same location where you place any web.xml file). |
The jboss-ejb-client.xml will look like:
<
jboss-ejb-client
xmlns
=
"urn:jboss:ejb-client:1.0"
>
<
client-context
>
<
ejb-receivers
>
<
remoting-ejb-receiver
outbound-connection-ref
=
"remote-ejb-connection"
/>
</
ejb-receivers
>
</
client-context
>
</
jboss-ejb-client
>
|
You'll notice that we have configured the EJB client context (for this application) to use a remoting-ejb-receiver which points to our earlier created "remote-outbound-connection" named "remote-ejb-connection". This links the EJB client context to use the "remote-ejb-connection" which ultimately points to the EJB remoting connector on the "Destination Server".
Let's deploy the client application on the "Client Server". The process of deploying the application is out of scope, of this chapter. You can use either the CLI or the admin console or a IDE or deploy manually to JBOSS_HOME/standalone/deployments folder. Just ensure that the application is deployed successfully.
We mentioned that we'll be using a servlet to invoke on the bean, but the code to invoke the bean isn't servlet specific and can be used in other components (like EJB) too. So let's see how it looks like:
import
javax.naming.Context;
import
java.util.Hashtable;
import
javax.naming.InitialContext;
...
public
void
invokeOnBean() {
try
{
final
Hashtable props =
new
Hashtable();
// setup the ejb: namespace URL factory
props.put(Context.URL_PKG_PREFIXES,
"org.jboss.ejb.client.naming"
);
// create the InitialContext
final
Context context =
new
javax.naming.InitialContext(props);
// Lookup the Greeter bean using the ejb: namespace syntax which is explained here https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI
final
Greeter bean = (Greeter) context.lookup(
"ejb:"
+
"myapp"
+
"/"
+
"myejb"
+
"/"
+
""
+
"/"
+
"GreeterBean"
+
"!"
+ org.myapp.ejb.Greeter.
class
.getName());
// invoke on the bean
final
String greeting = bean.greet(
"Tom"
);
System.out.println(
"Received greeting: "
+ greeting);
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
|
That's it! The above code will invoke on the bean deployed on the "Destination Server" and return the result.
This chapter details the extensions that are available when developing Enterprise Java Beans tm on JBoss Application Server 7.
Currently there is no support for configuring the extensions using an implementation specific descriptor file.
Each Message Driven Bean must be connected to a resource adapter.
The ResourceAdapter annotation is used to specify the resource adapter with which the MDB should connect.
The value of the annotation is the name of the deployment unit containing the resource adapter. For example jms-ra.rar.
For example:
@MessageDriven
(messageListenerInterface = PostmanPat.
class
)
@ResourceAdapter
(
"ejb3-rar.rar"
)
|
Whenever a run-as role is specified for a given method invocation the default anonymous principal is used as the caller principal. This principal can be overridden by specifying a run-as principal.
The RunAsPrincipal annotation is used to specify the run-as principal to use for a given method invocation.
The value of the annotation specifies the name of the principal to use. The actual type of the principal is undefined and should not be relied upon.
Using this annotation without specifying a run-as role is considered an error.
For example:
@RunAs
(
"admin"
)
@RunAsPrincipal
(
"MyBean"
)
|
Each Enterprise Java Bean tm can be associated with a security domain. Only when an EJB is associated with a security domain will authentication and authorization be enforced.
The SecurityDomain annotation is used to specify the security domain to associate with the EJB.
The value of the annotation is the name of the security domain to be used.
For example:
@SecurityDomain
(
"other"
)
|
For any newly started transaction a transaction timeout can be specified in seconds.
When a transaction timeout of 0 is used, then the actual transaction timeout will default to the domain configured default.
TODO: add link to tx subsystem
Although this is only applicable when using transaction attribute REQUIRED or REQUIRES_NEW the application server will not detect invalid setups.
New Transactions Take care that even when transaction attribute REQUIRED is specified, the timeout will only be applicable if a new transaction is started. |
The TransactionTimeout annotation is used to specify the transaction timeout for a given method.
The value of the annotation is the timeout used in the given unit granularity. It must be a positive integer or 0. Whenever 0 is specified the default domain configured timeout is used.
The unit specifies the granularity of the value. The actual value used is converted to seconds. Specifying a granularity lower than SECONDS is considered an error, even when the computed value will result in an even amount of seconds.
For example:@TransactionTimeout(value = 10, unit = TimeUnit.SECONDS)
The trans-timeout element is used to define the transaction timeout for business, home, component, and message-listener interface methods; no-interface view methods; web service endpoint methods; and timeout callback methods.
The trans-timeout element resides in the urn:trans-timeout namespace and is part of the standard container-transaction element as defined in the jboss namespace.
For the rules when a container-transaction is applicable please refer to EJB 3.1 FR 13.3.7.2.1.
xmlns:tx
=
"urn:trans-timeout"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd
urn:trans-timeout http://www.jboss.org/j2ee/schema/trans-timeout-1_0.xsd"
version
=
"3.1"
impl-version
=
"2.0"
>
<
assembly-descriptor
>
<
container-transaction
>
<
method
>
<
ejb-name
>BeanWithTimeoutValue</
ejb-name
>
<
method-name
>*</
method-name
>
<
method-intf
>Local</
method-intf
>
</
method
>
<
tx:trans-timeout
>
<
tx:timeout
>10</
tx:timeout
>
<
tx:unit
>Seconds</
tx:unit
>
</
tx:trans-timeout
>
</
container-transaction
>
</
assembly-descriptor
>
</
jboss:ejb-jar
>
|
The service is responsible to call the registered timeout methods of the different session beans.
A persistent timer will be identified by the name of the EAR, the name of the sub-deployment JAR and the Bean's name. If one of those names are changed (e.g. EAR name contain a version) the timer entry became orphaned and the timer event will not longer be fired. |
The timer is will be started once at the specified time.
In case of a server restart the timeout method of a persistent timer will only be called directly if the specified time is elapsed.
If the timer is not persistent (since EJB3.1 see 18.2.3) it will be not longer available if JBoss is restarted or the application is redeployed.
The timer will be started at the specified first occurrence and after that point at each time if the interval is elapsed.
If the timer will be started during the last execution is not finished the execution will be suppressed with a warning to avoid concurrent execution.
In case of server downtime for a persistent timer, the timeout method will be called only once if one, or more than one, interval is elapsed.
If the timer is not persistent (since EJB3.1 see 18.2.3) it will not longer be active after the server is restarted or the application is redeployed.
The timer will be started if the schedule expression match. It will be automatically deactivated and removed if there will be no next expiration possible, i.e. If you set a specific year.
For example:
@Schedule( ... dayOfMonth="1", month="1", year="2012")
// start once at 01-01-2012 00:00:00
If the timer is persistent it will be fetched at server start and the missed timeouts are called concurrent.
If a persistent timer contains an end date it will be executed once nevertheless how many times the execution was missed. Also a retry will be suppressed if the timeout method throw an Exception.
In case of such expired timer access to the given Timer object might throw a NoMoreTimeoutExcption or NoSuchObjectException.
If the timer is non persistent it will not longer be active after the server is restarted or the application is redeployed.
TODO: clarify whether this should happen concurrently/blocked or even fired only once like a recurring timer!
If the timer is non persistent it will not activated for missed events during the server is down. In case of server start the timer is scheduled based on the @Schedule annotation.
If the timer is persistent (default if not deactivated by annotation) all missed events are fetched at server start and the annotated timeout method is called concurrent.
TODO: clarify whether this should happen concurrently/blocked or even fired only once like a recurring timer!
The JBoss Application Server JPA subsystem implements the JPA 2.0 container-managed requirements. Deploys the persistence unit definitions, the persistence unit/context annotations and persistence unit/context references in the deployment descriptor. JPA Applications use the Hibernate (core) 4.0 persistence provider, that is included with JBoss AS. The JPA subsystem uses the standard SPI (javax.persistence.spi.PersistenceProvider) to access the Hibernate persistence provider and some additional extensions as well.
During application deployment, JPA use is detected (e.g. persistence.xml or @PersistenceContext/Unit annotations) and injects Hibernate dependencies into the application deployment. This makes it easy to deploy JPA applications.
In the remainder of this documentation, ”entity manager” refers to an instance of the javax.persistence.EntityManager class. Javadoc for the JPA interfaceshttp://download.oracle.com/javaee/6/api/javax/persistence/package-summary.html and JPA 2.0 specification. The index of Hibernate documentationis here.
The entity manager is similar to the Hibernate Session class; applications use it to create/read/update/delete data (and related operations). Applications can use application-managed or container-managed entity managers. Keep in mind that the entity manager is not expected to be thread safe (don't inject it into a servlet class variable which is visible to multiple threads).
Application-managed entity managers provide direct access to the underlying persistence provider (org.hibernate.ejb.HibernatePersistence). The scope of the application-managed entity manager is from when the application creates it and lasts until the app closes it. Use the @PersistenceUnit annotation to inject a persistence unit into ajavax.persistence.EntityManagerFactory. The EntityManagerFactory can return an application-managed entity manager.
Container-managed entity managers auto-magically manage the underlying persistence provider for the application. Container-managed entity managers may use transaction-scoped persistence contexts or extended persistence contexts. The container-managed entity manager will create instances of the underlying persistence provider as needed. Every time that a new underlying persistence provider (org.hibernate.ejb.HibernatePersistence) instance is created, a new persistence context is also created (as an implementation detail of the underlying persistence provider).
The JPA persistence context contains the entities managed by the persistence provider. The persistence context acts like a first level (transactional) cache for interacting with the datasource. Loaded entities are placed into the persistence context before being returned to the application. Entities changes are also placed into the persistence context (to be saved in the database when the transaction commits).
The transaction-scoped persistence context coordinates with the (active) JTA transaction. When the transaction commits, the persistence context is flushed to the datasource (entity objects are detached but may still be referenced by application code). All entity changes that are expected to be saved to the datasource, must be made during a transaction. Entities read outside of a transaction will be detached when the entity manager invocation completes. Example transaction-scoped persistence context is below.
@Stateful
// will use container managed transactions
public
class
CustomerManager {
@PersistenceContext
(unitName =
"customerPU"
)
// default type is PersistenceContextType.TRANSACTION
EntityManager em;
public
customer createCustomer(String name, String address) {
Customer customer =
new
Customer(name, address);
em.persist(customer);
// persist new Customer when JTA transaction completes (when method ends).
// internally:
// 1. Look for existing "customerPU" persistence context in active JTA transaction and use if found.
// 2. Else create new "customerPU" persistence context (e.g. instance of org.hibernate.ejb.HibernatePersistence)
// and put in current active JTA transaction.
return
customer;
// return Customer entity (will be detached from the persistence context when caller gets control)
}
// Transaction.commit will be called, Customer entity will be persisted to the database and "customerPU" persistence context closed
|
The Container-managed extended persistence context can span multiple transactions and allows data modifications to be queued up (like a shopping cart), without an active JTA transaction (to be applied during the next JTA TX). The Container-managed extended persistence context can only be injected into a stateful session bean.
@PersistenceContext
(type = PersistenceContextType.EXTENDED, unitName =
"inventoryPU"
)
EntityManager em;
|
JPA 2.0 makes it easy to use your (pojo) plain old Java class to represent a database table row.
@PersistenceContext
EntityManager em;
Integer bomPk = getIndexKeyValue();
BillOfMaterials bom = em.find(BillOfMaterials.
class
, bomPk);
// read existing table row into BillOfMaterials class
BillOfMaterials createdBom =
new
BillOfMaterials(
"..."
);
// create new entity
em.persist(createdBom);
// createdBom is now managed and will be saved to database when the current JTA transaction completes
|
The entity lifecycle is managed by the underlying persistence provider.
The persistence.xml contains the persistence unit configuration (e.g. datasource name) and as described in the JPA 2.0 spec (section 8.2), the jar file or directory whose META-INF directory contains the persistence.xml file is termed the root of the persistence unit. In Java EE environments, the root of a persistence unit must be one of the following (quoted directly from the JPA 2.0 specification):
The persistence.xml can specify either a JTA datasource or a non-JTA datasource. The JTA datasource is expected to be used within the EE environment (even when reading data without an active transaction). If a datasource is not specified, the default-datasource will instead be used (must be configured).
NOTE: Java Persistence 1.0 supported use of a jar file in the root of the EAR as the root of a persistence unit. This use is no longer supported. Portable applications should use the EAR library directory for this case instead.
The org.jboss.as.jpa logging can be enabled to get the following information:
To enable TRACE, open the as/standalone/configuration/standalone.xml (or as/domain/configuration/domain.xml) file. Search for <subsystem xmlns="urn:jboss:domain:logging:1.0"> and add the org.jboss.as.jpa category. You need to change the console-handler level from INFO to TRACE.
<
subsystem
xmlns
=
"urn:jboss:domain:logging:1.0"
>
<
console-handler
name
=
"CONSOLE"
>
<
level
name
=
"TRACE"
/>
...
</
console-handler
>
</
periodic-rotating-file-handler
>
<
logger
category
=
"com.arjuna"
>
<
level
name
=
"WARN"
/>
</
logger
>
<
logger
category
=
"org.jboss.as.jpa"
>
<
level
name
=
"TRACE"
/>
</
logger
>
<
logger
category
=
"org.apache.tomcat.util.modeler"
>
<
level
name
=
"WARN"
/>
</
logger
>
...
|
To troubleshoot issues with the Hibernate second level cache, try enabling trace for org.hibernate.SQL + org.hibernate.cache.infinispan + org.infinispan:
<subsystem xmlns=
"urn:jboss:domain:logging:1.0"
>
<console-handler name=
"CONSOLE"
>
<level name=
"TRACE"
/>
...
</console-handler>
</periodic-rotating-file-handler>
<logger category=
"com.arjuna"
>
<level name=
"WARN"
/>
</logger>
<logger category=
"org.hibernate.SQL"
>
<level name=
"TRACE"
/>
</logger>
<logger category=
"org.hibernate"
>
<level name=
"TRACE"
/>
</logger>
<logger category=
"org.infinispan"
>
<level name=
"TRACE"
/>
</logger>
<logger category=
"org.apache.tomcat.util.modeler"
>
<level name=
"WARN"
/>
</logger>
...
|
Hibernate 4 is packaged with the AS and is the default persistence provider.
To enable the second level cache with Hibernate 4, just set the hibernate.cache.use_second_level_cache property to true, as is done in the following example (also set the shared-cache-mode accordingly). By default the application server uses Infinispan as cache provider, so you don't need specify anything on top of that:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?><
persistence
xmlns
=
"http://java.sun.com/xml/ns/persistence"
version
=
"1.0"
>
<
persistence-unit
name
=
"2lc_example_pu"
>
<
description
>example of enabling the second level cache.</
description
>
<
jta-data-source
>java:jboss/datasources/mydatasource</
jta-data-source
>
<
shared-cache-mode
>ENABLE_SELECTIVE</
shared-cache-mode
>
<
properties
>
<
property
name
=
"hibernate.cache.use_second_level_cache"
value
=
"true"
/>
</
properties
>
</
persistence-unit
>
</
persistence
>
|
Here is an example of enabling the second level cache for a Hibernate native API hibernate.cfg.xml file:
<property name=
"hibernate.cache.region.factory_class"
value=
"org.jboss.as.jpa.hibernate4.infinispan.InfinispanRegionFactory"
/>
<property name=
"hibernate.cache.infinispan.cachemanager"
value=
"java:jboss/infinispan/container/hibernate"
/>
<property name=
"hibernate.transaction.manager_lookup_class"
value=
"org.hibernate.transaction.JBossTransactionManagerLookup"
/>
<property name=
"hibernate.cache.use_second_level_cache"
value=
"true"
/>
|
The Hibernate native API application will also need a MANIFEST.MF:
Dependencies: org.infinispan,org.hibernate
|
Infinispan Hibernate/JPA second level cache provider documentation contains advanced configuration information but you should bear in mind that when Hibernate runs within JBoss Application Server 7, some of those configuration options, such as region factory, are not needed. Moreover, the application server providers you with option of selecting a different cache container for Infinispan via hibernate.cache.infinispan.container persistence property. To reiterate, this property is not mandatory and a default container is already deployed for by the application server to host the second level cache.
Just update the current as7/modules/org/hibernate/main folder to contain the newer version (after stopping your AS7 server instance). The following instructions assume you are bringing in Hibernate ORM 4.1.1 jars.
Updated as7/modules/org/hibernate/main/module.xml will look like (note that dependencies won't change):
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright
2011
, Red Hat, Inc., and individual contributors
~ as indicated by the
@author
tags. See the copyright.txt file in the
~ distribution
for
a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version
2.1
of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License
for
more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with
this
software;
if
not, write to the Free
~ Software Foundation, Inc.,
51
Franklin St, Fifth Floor, Boston, MA
~
02110
-
1301
USA, or see the FSF site: http:
//www.fsf.org.
-->
<!-- Represents the Hibernate
4.0
.x module-->
<module xmlns=
"urn:jboss:module:1.1"
name=
"org.hibernate"
>
<resources>
<resource-root path=
"hibernate-core-4.1.1.Final.jar"
/>
<resource-root path=
"hibernate-commons-annotations-4.0.1.Final.jar"
/>
<resource-root path=
"hibernate-entitymanager-4.1.1.Final.jar"
/>
<resource-root path=
"hibernate-infinispan-4.1.1.Final.jar"
/>
<!-- Insert resources here -->
</resources>
<dependencies>
.
.
.
</dependencies>
</module>
|
Updated as7/modules/org/hibernate/envers/module.xml will look like (note that dependencies won't change):
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright
2011
, Red Hat, Inc., and individual contributors
~ as indicated by the
@author
tags. See the copyright.txt file in the
~ distribution
for
a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version
2.1
of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License
for
more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with
this
software;
if
not, write to the Free
~ Software Foundation, Inc.,
51
Franklin St, Fifth Floor, Boston, MA
~
02110
-
1301
USA, or see the FSF site: http:
//www.fsf.org.
-->
<module xmlns=
"urn:jboss:module:1.1"
name=
"org.hibernate.envers"
>
<resources>
<resource-root path=
"hibernate-envers-4.1.1.Final.jar"
/>
<!-- Insert resources here -->
</resources>
<dependencies>
.
.
.
</dependencies>
</module>
|
AS7 allows the packaging of Hibernate 3.5 (or greater) persistence provider jars with the application. The JPA deployer will detect the presence of a persistence provider in the application and jboss.as.jpa.providerModule needs to be set to hibernate3-bundled.
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<persistence-unit name=
"plannerdatasource_pu"
>
<description>Hibernate
3
Persistence Unit.</description>
<jta-data-source>java:jboss/datasources/PlannerDS</jta-data-source>
<properties>
<property name=
"hibernate.show_sql"
value=
"false"
/>
<property name=
"jboss.as.jpa.providerModule"
value=
"hibernate3-bundled"
/>
</properties>
</persistence-unit>
</persistence>
|
The AS7 testsuite contains a test that packages jars from the Hibernate 3.6.5.Final jars in the ear lib.
Applications can share the same Hibernate3 (for Hibernate 3.5 or greater) persistence provider by manually creating an org.hibernate:3 module (in the AS/modules folder). Steps to create the Hibernate3 module:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
module
xmlns
=
"urn:jboss:module:1.0"
name
=
"org.hibernate"
slot
=
"3"
>
<
resources
>
<
resource-root
path
=
"hibernate3-core.jar"
/>
<
resource-root
path
=
"hibernate3-commons-annotations.jar"
/>
<
resource-root
path
=
"hibernate3-entitymanager.jar"
/>
<
resource-root
path
=
"javassist-3.12.0.GA.jar"
/>
<
resource-root
path
=
"antlr-2.7.6.jar"
/>
<
resource-root
path
=
"commons-collections-3.1.jar"
/>
<
resource-root
path
=
"dom4j-1.6.1.jar"
/>
<
resource-root
path
=
"javassist-3.12.0.GA.jar"
/>
<!-- Insert other Hibernate 3 jars to be used here -->
</
resources
>
<
dependencies
>
<
module
name
=
"org.jboss.as.jpa.hibernate"
slot
=
"3"
/>
<
module
name
=
"asm.asm"
/>
<
module
name
=
"javax.api"
/>
<
module
name
=
"javax.persistence.api"
/>
<
module
name
=
"javax.transaction.api"
/>
<
module
name
=
"javax.validation.api"
/>
<
module
name
=
"org.infinispan"
/>
<
module
name
=
"org.javassist"
/>
<
module
name
=
"org.slf4j"
/>
</
dependencies
>
</
module
>
|
In your persistence.xml, you will refer to the Hibernate 3 persistence provider as follows:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
persistence-unit
name
=
"crm_pu"
>
<
description
>CRM Persistence Unit.</
description
>
<
jta-data-source
>java:jboss/datasources/CRMDS</
jta-data-source
>
<
properties
>
<
property
name
=
"jboss.as.jpa.providerModule"
value
=
"org.hibernate:3"
/>
</
properties
>
</
persistence-unit
>
</
persistence
>
|
Read the How to use EclipseLink with AS7 guide here.
Applications that use the Hibernate API directly, are referred to here as native Hibernate applications. Native Hibernate applications, can choose to use the Hibernate jars included with JBoss AS or they can package their own copy of the Hibernate jars. Applications that utilize JPA will automatically have the JBoss AS Hibernate injected onto the application deployment classpath. Meaning that JPA applications, should expect to use the Hibernate jars included in JBoss AS.
Example MANIFEST.MF entry to add Hibernate dependency:
Manifest-Version:
1.0
...
Dependencies: org.hibernate, org.hibernate.envers
|
You can inject a org.hibernate.Session and org.hibernate.SessionFactory directly, just as you can do with EntityManagers and EntityManagerFactorys.
import
org.hibernate.Session;
import
org.hibernate.SessionFactory;
@Stateful
public
class
MyStatefulBean ... {
@PersistenceContext
(unitName=
"crm"
) Session session1;
@PersistenceContext
(unitName=
"crm2"
, type=EXTENDED) Session extendedpc;
@PersistenceUnit
(unitName=
"crm"
) SessionFactory factory;
}
|
AS7 automatically sets the following Hibernate (4.x) properties:
Property | Purpose |
---|---|
hibernate.id.new_generator_mappings=true | New applications should let this default to true, older applications with existing data might need to set to false (see note below). It really depends on whether your application uses the @GeneratedValue(AUTO) which will generates new key values for newly created entities. The application can override this value (in the persistence.xml). |
hibernate.transaction.jta.platform= instance of org.hibernate.service.jta.platform.spi.JtaPlatform interface | The transaction manager, user transaction and transaction synchronization registry is passed into Hibernate via this class. |
hibernate.ejb.resource_scanner = instance of org.hibernate.ejb.packaging.Scanner interface | Instance of entity scanning class is passed in that knows how to use the AS annotation indexer (for faster deployment). |
hibernate.transaction.manager_lookup_class | This property is removed if found in the persistence.xml (could conflict with JtaPlatform) |
hibernate.session_factory_name = qualified persistence unit name | Is set to the application name + persistence unit name (application can specify a different value but it needs to be unique across all application deployments on the AS instance). |
hibernate.session_factory_name_is_jndi = false | only set if the application didn't specify a value for hibernate.session_factory_name. |
hibernate.ejb.entitymanager_factory_name = qualified persistence unit name | Is set to the application name + persistence unit name (application can specify a different value but it needs to be unique across all application deployments on the AS instance). |
In Hibernate 4.x, if new_generator_mappings is true:
In Hibernate 4.x, if new_generator_mappings is false:
The following properties are supported in the persistence unit definition (in the persistence.xml file):
Property | Purpose |
---|---|
jboss.as.jpa.providerModule | is the name of the persistence provider module (default is org.hibernate). Should be hibernate3-bundled if Hibernate 3 jars are in the application archive (adapterModule and adapterClass will automatically be set for hibernate3-bundled). Should be application, if a persistence provider is packaged with the application. |
jboss.as.jpa.adapterModule | is the name of the integration classes that help the AS to work with the persistence provider. Current valid values areorg.jboss.as.jpa.hibernate:4 (Hibernate 4 integration classes) and org.jboss.as.jpa.hibernate:3 (Hibernate 3 integration classes). Other integration adapter modules are expected to be added. |
jboss.as.jpa.adapterClass | is the class name of the integration adapter. Current valid values areorg.jboss.as.jpa.hibernate3.HibernatePersistenceProviderAdaptor andorg.jboss.as.jpa.hibernate4.HibernatePersistenceProviderAdaptor. |
jboss.as.jpa.managed | can be set to false to disable container managed JPA access to the persistence unit. The default is true, which enables container managed JPA access to the persistence unit. This is typically set to false for Seam 2.x + Spring applications. |
By default JBoss AS7 does not bind the entity manager factory to JNDI. However, you can explicitly configure this in the persistence.xml of your application by setting thejboss.entity.manager.factory.jndi.name property. The value of that property should be the JNDI name to which the entity manager factory should be bound. Here's an example:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
persistence
version
=
"2.0"
xmlns
=
"http://java.sun.com/xml/ns/persistence"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
>
<
persistence-unit
name
=
"myPU"
>
<!-- If you are running in a production environment, add a managed
data source, the example data source is just for proofs of concept! -->
<
jta-data-source
>java:jboss/datasources/ExampleDS</
jta-data-source
>
<
properties
>
<!-- Bind entity manager factory to JNDI at java:jboss/myEntityManagerFactory -->
<
property
name
=
"jboss.entity.manager.factory.jndi.name"
value
=
"java:jboss/myEntityManagerFactory"
/>
</
properties
>
</
persistence-unit
>
</
persistence
>
|
Many thanks to the community, for reporting issues, solutions and code changes. A number of people have been answering AS7 forum questions related to JPA usage. I would like to thank them for this, as well as those reporting issues. For those of you that haven't downloaded the AS source code and started hacking patches together. I would like to encourage you to start by reading Hacking on AS7. You will find that it easy very easy to find your way around the AS7/JPA/* source tree and make changes. The following list of contributors should grow over time, I hope to see more of you listed here.
If you see someone answering JPA related questions in the forums,or if I left you out (and you have been answering JPA questions), add the name to the above list.
The OSGi Developer Guide can be found here: OSGi User and Developer Guide.
JBoss AS offers several mechanisms to retrieve components by name. Every JBoss AS instance has it's own local JNDI namespace (java:) which is unique per JVM. The layout of this namespace is primarily governed by the Java EE specification. Applications which share the same JBoss AS instance can use this namespace to intercommunicate. In addition to local JNDI, a variety of mechanisms exist to access remote components.
The Java EE platform specification defines the following JNDI contexts:
In addition to the standard namespaces, AS7 also provides the following two global namespaces:
AS 7.1 also provides the java:jboss/exported/ context, entries bound to this context are accessible over remote JNDI.
For web deployments java:comp/ is aliased to java:module/, so EJBs deployed in a WAR do not have their own component-scoped namespace. |
There are several methods that can be used to bind entries into JNDI in AS7.
Use the @EJB annotation to specify additional JNDI names for a bean. The bean below is bound to"java:global/MyBean" as well as the standard portable JNDI names.
package
com.example;
import
javax.ejb.EJB;
import
javax.ejb.Stateless;
@Stateless
@EJB
(name =
"java:global/MyBean"
, beanInterface = MyBean.
class
)
public
class
MyBean
{
}
|
For Java EE applications the recommended way is to use a deployment descriptor to create the binding. For example the following web.xml binds the string "Hello World"to java:global/mystring and the string "Hello Module" to java:comp/env/hello (which is aliased to java:module/env/hello, as this is a war deployment, sojava:comp is the same as java:module).
<
web-app
version
=
"3.0"
xsi:schemaLocation
=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
>
<
env-entry
>
<
env-entry-name
>java:global/mystring</
env-entry-name
>
<
env-entry-type
>java.lang.String</
env-entry-type
>
<
env-entry-value
>Hello World</
env-entry-value
>
</
env-entry
>
<
env-entry
>
<
env-entry-name
>hello</
env-entry-name
>
<
env-entry-type
>java.lang.String</
env-entry-type
>
<
env-entry-value
>Hello Module</
env-entry-value
>
</
env-entry
>
</
web-app
>
|
For more details, see the Java EE Platform Specification or a Java EE tutorial.
Entries can also be bound to JNDI using Context.bind() in the normal fashion. The application server tracks which bindings belong to a deployment, and the bindings are automatically removed when the deployment is undeployed.
It is also possible to bind to one of the three global namespaces using configuration in the naming subsystem. This can be done by either editing thestandalone.xml/domain.xml file directly, or through the management API.
Three different types of bindings are supported:
An example standalone.xml might look like:
<
subsystem
xmlns
=
"urn:jboss:domain:naming:1.1"
>
<
bindings
>
<
simple
name
=
"java:global/a"
value
=
"100"
type
=
"int"
/>
<
object-factory
name
=
"java:global/b"
module
=
"com.acme"
class
=
"org.acme.MyObjectFactory"
/>
<
lookup
name
=
"java:global/c"
lookup
=
"java:global/b"
/>
</
bindings
>
</
subsystem
>
|
To add these entries via the CLI:
/subsystem=naming/binding=java\:global\/mybinding:add(binding-type=simple, type=
long
, value=
1000
)
|
To see all all options that are taken by the add command (this can actually be used to get the description of any CLI command):
/subsystem=naming/binding=*:read-operation-description(name=add)
|
AS 7.1 supports two different types of remote JNDI. The old jnp based JNDI implementation used in previous JBoss versions is no longer supported.
The remote: protocol uses the JBoss remoting protocol to lookup items from the servers local JNDI. To use it, you must have the appropriate jars on the class path, if you are maven user can be done simply by adding the following to your pom.xml:
<
dependency
>
<
groupId
>org.jboss.as</
groupId
>
<
artifactId
>jboss-as-ejb-client-bom</
artifactId
>
<
version
>7.1.1.Final</
version
>
<
type
>pom</
type
>
<
scope
>compile</
scope
>
</
dependency
>
|
If you are not using maven a shaded jar that contains all required classes
can be found in the bin/client directory of the AS 7.1 distribution.
final
Properties env =
new
Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, org.jboss.naming.remote.client.InitialContextFactory.
class
.getName());
env.put(Context.PROVIDER_URL,
"remote://localhost:4447"
);
remoteContext =
new
InitialContext(env);
|
The ejb: namespace is provided by the jboss-ejb-client library. This protocol allows you to look up EJB's, using their application name, module name, ejb name and interface type.
This is a client side JNDI implementation. Instead of looking up an EJB on the server the lookup name contains enough information for the client side library to generate a proxy with the EJB information. When you invoke a method on this proxy it will use the current EJB client context to perform the invocation. If the current context does not have a connection to a server with the specified EJB deployed then an error will occur. Using this protocol it is possible to look up EJB's that do not actually exist, and no error will be thrown until the proxy is actually used. The exception to this is stateful session beans, which need to connect to a server when they are created in order to create the session bean instance on the server.
Some examples are:
ejb:myapp/myejbjar/MyEjbName!com.test.MyRemoteInterface
ejb:myapp/myejbjar/MyStatefulName!comp.test.MyStatefulRemoteInterface?stateful
The first example is a lookup of a singleton, stateless or EJB 2.x home interface. This lookup will not hit the server, instead a proxy will be generated for the remote interface specified in the name. The second example is for a stateful session bean, in this case the JNDI lookup will hit the server, in order to tell the server to create the SFSB session.
For more details on how the server connections are configured, please see
EJB invocations from a remote client using JNDI.
This document details the main points that need to be considered by Spring developers that wish to develop new applications or to migrate existing applications to be run into JBoss Application Server 7 (AS7).
AS7 has a modular class loading strategy, different from previous versions of JBoss AS, which enforces a better class loading isolation between deployments and the application server itself. A detailed description can be found in the documentation dedicated to class loading in AS7.
This reduces significantly the risk of running into a class loading conflict and allows applications to package their own dependencies if they choose to do so. This makes it easier for Spring applications that package their own dependencies - such as logging frameworks or persistence providers to run on JBoss AS.
At the same time, this does not mean that duplications and conflicts cannot exist on the classpath. Some module dependencies are implicit, depending on the type of deployment as shown here.
Depending on the strategy being used, Spring applications can be:
Applications that use the Hibernate API directly with Spring (i.e. through either one of LocalSessionFactoryBean or AnnotationSessionFactoryBean) may use a version of Hibernate 3 packaged inside the application. Hibernate 4 (which is provided through the 'org.hibernate' module of AS7) is not supported by Spring 3.0 and Spring 3.1 (and may be supported by Spring 3.2 as described in SPR-8096), so adding this module as a dependency is not a solution.
Spring applications using JPA may choose between:
Applications that use a server-deployed persistence unit must observe the typical Java EE rules in what concerns dependency management, i.e. the javax.persistence classes and persistence provider (Hibernate) are contained in modules which are added automatically by the application when the persistence unit is deployed.
In order to use the server-deployed persistence units from within Spring, either the persistence context or the persistence unit need to be registered in JNDI via web.xml as follows:
<
persistence-context-ref
>
<
persistence-context-ref-name
>persistence/petclinic-em</
persistence-context-ref-name
>
<
persistence-unit-name
>petclinic</
persistence-unit-name
>
</
persistence-context-ref
>
|
or, respectively:
<
persistence-unit-ref
>
<
persistence-unit-ref-name
>persistence/petclinic-emf</
persistence-unit-ref-name
>
<
persistence-unit-name
>petclinic</
persistence-unit-name
>
</
persistence-unit-ref
>
|
When doing so, the persistence context or persistence unit are available to be looked up in JNDI, as follows:
<jee:jndi-lookup id=
"entityManager"
jndi-name=
"java:comp/env/persistence/petclinic-em"
expected-type=
"javax.persistence.EntityManager"
/>
|
or
<jee:jndi-lookup id=
"entityManagerFactory"
jndi-name=
"java:comp/env/persistence/petclinic-emf"
expected-type=
"javax.persistence.EntityManagerFactory"
/>
|
JNDI binding JNDI binding via persistence.xml properties is not supported in JBoss AS 7.0 |
Spring applications running in JBoss AS7 may also create persistence units on their own, using the LocalContainerEntityManagerFactoryBean. This is what these applications need to consider:
When the application server encounters a deployment that has a file named META-INF/persistence.xml (or, for that matter, WEB-INF/classes/META-INF/persistence.xml), it will attempt to create a persistence unit based on what is provided in the file. In most cases, such definition files are not compliant with the Java EE requirements, mostly because required elements such as the datasource of the persistence unit are supposed to be provided by the Spring context definitions, which will fail the deployment of the persistence unit, and consequently of the entire deployment.
Spring applications can easily avoid this type of conflict, by using a feature of the LocalContainerEntityManagerFactoryBean which is designed for this purpose. Persistence unit definition files can exist in other locations than META-INF/persistence.xml and the location can be indicated through the persistenceXmlLocation property of the factory bean class.
Assuming that the persistence unit is in the META-INF/jpa-persistence.xml, the corresponding definition can be:
<bean id=
"entityManagerFactory"
class
=
"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
>
<property name=
"persistenceXmlLocation"
value=
"classpath*:META-INF/jpa-persistence.xml"
/>
<!-- other definitions -->
</bean>
|
Since the LocalContainerEntityManagerFactoryBean and the corresponding HibernateJpaVendorAdapter are based on Hibernate 3, it is required to use that version with the application. Therefore, the Hibernate 3 jars must be included in the deployment. At the same time, due the presence of @PersistenceUnit or @PersistenceContext annotations on the application classes, the application server will automatically add the 'org.hibernate' module as a dependency.
This can be avoided by instructing the server to exclude the module from the deployment's list of dependencies. In order to do so, include a META-INF/jboss-deployment-structure.xml or, for web applications, WEB-INF/jboss-deployment-structure.xml with the following content:
<jboss-deployment-structure xmlns=
"urn:jboss:deployment-structure:1.0"
>
<deployment>
<exclusions>
<module name=
"org.hibernate"
/>
</exclusions>
</deployment>
</jboss-deployment-structure>
|