Packaging your applications
Modules supported by an EE server
Description | Descriptor | Contents |
---|---|---|
EJB Java Archive (EJB-JAR) | META-INF/ejb-jar.xml or WEB-INF/ejb-jar.xml | Session and message-driven beans. Optionally JPA and CDI may be included as well. |
Web Application Archive (WAR) | WEB-INF/web.xml | Web application artifacts such as Servlets, JSPs, JSF, static images, and so on. Optionally EJBs, JPA, and CDI may be included as well. |
Enterprise Application Archive (EAR) | META-INF/application.xml | Other Java EE modules such as EJB-JARs and WARs. |
Resource Adapter Archive (RAR) | META-INF/ra.xml | Resource adapters. |
Client Application Archives (CAR) | META-INF/ application-client.xml | Standalone Java client for EJBs. |
Java Persistence Archive (JPA) | META-INF/persistence.xml or WEB-INF/persistence.xml | Java EE standard ORM between applications and databases. May be included as part of the following archives: EJB-JAR, WAR, EAR, and CAR. |
Context Dependency and Injection Bean Archive (CDI) | META-INF/bean.xml or WEB-INF/bean.xml | Java EE standard dependency injection. May be included as part of the following archives: EJB-JAR, WAR, EAR, and CAR. |
Deployment descriptors versus annotations
Conversation-over-configuration as well as in-code annotation has taken over the heavy lifting that deployment descriptors userd to do when it came to configuring EE modules.
But there are still cases where deployment descriptors are not only useful but required.
For example, when using CDI, your archive must contain a META-INF/bean.xml
or WEB-INF/beans.xml
, even if the file is empty, to indicate that you application uses CDI.
Dissecting the Java EE module system
The file structure of the EAR module for ActionBazaar looks like this:
META-INF/application.xml
actionBazaar-ejb.jar
actionBazaar.war
actionBazaar-client.jar
lib/actionBazaar-commons.jar
The contents of application.xm
l look something like the following listing.
When you deploy this EAR to an application server, the application server uses the information in the deployment descriptor to deploy each of the module types.
Loading a Java EE module
During the deployment process, the application server determines the module types, validates them, and takes appropriate steps so that the application is available to users.
- Rules followed by application servers to deploy an EAR module. Java EE doesn’t require a deployment descriptor in the EAR module to identify the types of modules packaged. It’s the responsibility of the Java EE server to determine the type of module based on its naming conventions (extension) and its content. It does so by following this algorithm.
Exploring class loading
Class-loading basics
Class loading in Java EE applications
An EAR module consisting of multiple EJB-JARs, WARs, and RARs, as well as supporting third-party libraries, will contain thousands of classes. It’s up to the Java EE server to provide a class-loading strategy that ensures all the applications can resolve the classes they need.
The EAR module class loader is extended from the application server class loader. This class loader loads the classes that are deployed at an EAR level. By default, they’re the classes packaged inside JARs in the /lib
directory of the EAR, but the default /lib
directory can be overridden by the library-directory
element in the application.xml
deployment descriptor.
Dependencies between Java EE modules
EJB-JAR
An EJB-JAR module must have access to the following:
- The contents and Class-Path of any external resource adaptor
- The contents and Class-Path of each library in the
/lib
directory of the EAR - The contents and Class-Path of the EJB-JAR module itself
- The contents and Class-Path of additional EJB-JAR modules deployed with the EAR
- The contents and Class-Path of any resource adaptor deployed with the EAR
WAR
A WAR module must have access to the following:
- The contents and Class-Path of any external resource adaptor
- The contents and Class-Path of each library in the
/lib
directory of the EAR - The contents and Class-Path of each library in the
/WEB-INF/lib
directory of the WAR - The contents of the /WEB-INF/classes directory of the WAR
- The Class-Path of the WAR
- The contents and Class-Path of all EJB-JAR modules deployed with the EAR
- The contents and Class-Path of any resource adaptor deployed with the EAR
Packaging session and message-driven beans
Packaging EJB-JAR
An EJB-JAR module is really nothing more than a Java JAR archive. With Java EE’s emphasis on convention-over-configuration and its use of annotations, the EJB-JAR module doesn’t even need to include the META-INF/ejb-jar.xml
deployment descriptor.
POM file to build an EJB-JAR
Packaging EJB in WAR
The Java EE 6 specification allows for session and message-driven beans to be included inside a WAR module instead of having to deploy them separately in an EJB-JAR module.
A WAR module will contain classes and other resources in a /classes
subdirectory, third-party dependencies in a /lib
subdirectory, and the configuration in /WEB-INF/web.xml
. A WEB module’s structure may look like this:
Using remote EJBs in WAR modules
Although including EJBs directly inside a WAR module is a great convenience started with the Java EE 6 spec, it’s not the only way to include EJBs inside a WAR module. It’s very typical (particularly in high-use and high-availability applications) to need much more business rules processing power than web front-end display power. Therefore, the architecture of the application is divided into a small cluster of web front-end servers running the WAR module and a much larger cluster of back-end serv- ers running the EJB modules. With two separate clusters, the WAR modules on the front end talk to the EJB modules on the back end remotely through @Remote
EJBs.
If your application is architected like this, the Maven configuration of the maven-ejb- plugin can include the <generateClient>true</generateClient>
option. With this option, Maven will package up all interfaces for the EJBs into its own JAR file. Your WAR modules can then include this dependency, which has only the interfaces instead of the entire EJB-JAR.
XML versus annotations
An EJB deployment descriptor (ejb-jar.xml
) describes the contents of an EJB module, any resources used by it, and security transaction settings.
An EJB-JAR module may contain
- A deployment descriptor (
ejb-jar.xml
) - A vendor-specific deployment descriptor, which is required to perform certain configuration settings in a particular EJB container
Be aware that the deployment descriptor is the final source and overrides settings provided through metadata annotations.
One-to-one mapping between annotations and XML descriptor elements
Annotation | Type | Annotation element | Corresponding descriptor element |
---|---|---|---|
@Stateless | EJB type | <session-type>Stateless | |
name | ejb-name | ||
@Stateful | EJB type | <session-type>Stateful | |
name | ejb-name | ||
@MessageDriven | EJB type | message-driven | |
name | ejb-name | ||
@Remote | Interface type | remote | |
@Local | Interface type | Local | |
@TransactionManagement | Transaction management type at bean level | transaction-type | |
@TransactionAttribute | Transaction settings method | container-transaction trans-attribute | |
@Interceptors | Interceptors | interceptor-binding interceptor-class | |
@ExcludeClassInterceptors | Interceptors | exclude-classinterceptor | |
@ExcludeDefaultInterceptors | Interceptors | exclude-defaultinterceptors | |
@AroundInvoke | Custom interceptor | around-invoke | |
@PostActivate | Lifecycle method | post-activate | |
@PrePassivate | Lifecycle method | pre-passivate | |
@DeclareRoles | Security setting | security-role | |
@RolesAllowed | Security setting | method-permission | |
@PermitAll | Security setting | unchecked | |
@DenyAll | Security setting | exclude-list | |
@RunAs | Security setting | security-identity run-as | |
@Resource | Resource references (DataSource, JMS, environment, mail, and so on) | resource-ref resource-env-ref message-destination-ref env-ref | |
Resource injection | Setter/field injection | injection-target | |
@EJB | EJB references | ejb-ref ejb-local-ref | |
@Persistence-Context | Persistence context reference | persistence-context-ref | |
@PersistenceUnit | Persistence unit reference | persistence-unit-ref |
Overriding annotations with XML
Suppose you have a stateless session bean that uses these annotations:
@Stateless(name = "BazaarAdmin")
public class BazaarAdminBean implements BazaarAdmin {
...
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public Item addItem() {...}
}
ejb-name
element specified in the deployment descriptor:
<ejb-name>BazaarAdmin</ejb-name>
If you don’t specify the name element, the container will use the name BazaarAdminBean
as the name of the bean class, and to override annotations you’ll have to use that name in the deployment descriptor:
<ejb-name>BazaarAdminBean</ejb-name>
You used @TransactionAttribute
to specify that the transaction attribute for a bean method be REQUIRES_NEW
. If you want to override it to use REQUIRED
, then use the fol- lowing descriptor:
Spcifying defaul interceptors
JPA packaging
Persistence module
JPA entities may also be packaged as a regular JAR archive and deployed in the root of the EAR module. The key is the META-INF/persistence.xml
file that designates the JAR archive, EJB-JAR module, or WAR module as containing one or more persistence units. We’ll take a quick look at how to properly package JPA classes and the persistence.xml
file.
JAR
EJB-JAR
WAR
Packaging JPA entities in a WAR module can be a bit tricky. There are two ways to do it.
- The first and easiest way is to put the JAR containing your JPA entities into the WAR module
WEB-INF/lib
directory.
ActionBazaar-web.war:
WEB-INF/
classes/
...
lib/
ActionBazaar.jar
web.xml
...
- The next listing shows how to package JPA inside an EAR module
But if the JPA entities aren’t in a separate JAR but are instead directly part of the WAR module itself, the following listing shows how this should be done.
Persistence unit scoping
You can define a persistence unit in a WAR, EJB-JAR, or JAR at the EAR level.
- If you define a persistence unit in a module, it’s visible only to that specific module.
- But if you define the unit by placing a JAR file in the root or lib directory of the EAR, the persistence unit will automatically be visible to all modules in the EAR.
For this to work, you must remember the restriction that if the same name is used by a persistence unit in the EAR level and at the module level, the persistence unit in the module level will win. Assume you have an EAR file structure like this:
lib/actionBazaar-common.jar
actionBazaar-ejb.jar
actionBazaar-web.war
The actionBazaar-common.jar
has a persistence unit with the name actionBazaar
and actionBazaar-ejb.jar
also has a persistence unit with the name actionBazaar
. The actionBazaar
persistence unit is automatically visible to the web module, and you can use it as follows:
@PersistenceUnit(unitName = "actionBazaar")
private EntityManagerFactory emf;
But if you use this code in the EJB module, the local persistence unit will be accessed because the local persistence unit has precedence. If you want to access the persistence unit defined at the EAR level, you have to reference it with the specific name as follows:
PersistenceUnit(unitName ="lib/actionBazaar-common.jar#actionBazaar")
private EntityManagerFactory emf;
Describing the persistence module with persistence.xml
CDI packaging
CDI 1.1 for Java EE 7 is defined in JSR 346 (jcp.org/en/jsr/deta…), which extends JSR 299 and JSR 330. CDI 1.1 adds specific requirements for dependency injection in a Java EE environment.
CDI modules
Bean discovery is simply looking for the beans.xml
file in the following locations:
META-INF/beans.xml
in any JAR, EJB-JAR, application client JAR, or RAR in the EAR or in any JAR archive or directory referred to by any of them by the ClassPath of theirMETA-INF/MANIFEST.MF
WEB-INF/beans.xml
of a WAR- Any directory on the JVM class path with
META-INF/beans.xml
Using the beans.xml
deployment descriptor
JAR
- Structure of a JAR archive marked for CDI bean discovery
EJB-JAR
WAR
Using the bean-discovery-mode annotation
Values for the bean-discovery-mode annotation
Value | Description |
---|---|
ALL | All types are processed. This behavior is the same as including the beans.xml in a Java EE 6 application. |
ANNOTATED | Only types with bean-defining annotations are processed. This is the default Java EE 7 behavior. |
NONE | All types in the archive (JAR) will be ignored. |
In a Java EE 7 application, there’s no need to include a beans.xml
file. With no beans.xml
file, CDI 1.1 will default to a beans discovery mode of ANNOTATED
.
Best practices and common deployment issues
Packaging and deployment best practices
- Start small. Start small when working on packaging your application and deploying it. Don’t work for a month, generate hundreds of beans, and then try to package and deploy your application for the first time. If there are problems with the packaging, it’s easier to solve them on a small deployment than a larger one.
- Use a constant server environment.
- Separate code from configuration packaging
- Understand your application and its dependencies
- Avoid using proprietary APIs and annotations. Don’t use vendor-specific tags or annotations unless it’s the only way to accomplish your task.
- Leverage your database administrator (DBA)
- Use your build tools
Troubleshooting common deployment problems
ClassNotFoundException
occurs when you’re attempting to dynamically load a resource that can’t be found. The reason for this exception can be a missing library at the correct loader level—you know, the JAR file containing the class that can’t be found. If you’re loading a resource or property file in your application, make sure you useThread.currentThread().getContextClassLoader().getResourceAsStream()
. ■NoClassDefFoundException
is thrown when code tries to instantiate an object or when dependencies of a previously loaded class can’t be resolved. Typically you run into this issue when all dependent libraries aren’t at the same classloader level.ClassCastException
normally is the result of duplication of classes at different levels. This occurs in the same-class, different-loader situation; that is, you try to cast a class loaded by class loader L1 with another class instance loaded by class loader L2. 'NamingException
is typically thrown when a JNDI lookup fails, because the container tries to inject a resource for an EJB that doesn’t exist. The stack trace for this exception gives the details about which lookup is failing. Make sure that your dependencies on data sources, EJBs, and other resources resolve properly.NotSerializableException
is thrown when an object needs to be moved from in-memory to some kind of byte[] form but the object doesn’t support this conversion. This can happen if stateful session beans need to be passivated and saved to disk to free up memory, or it can happen if session beans are accessed remotely and the objects they return need to be transferred over the network. Whatever the reason, if the object isn’t serializable, you’ll get this exception. The best way to avoid this is to add a JUnit test to assert the object is serializ- able. Typically objects start life with the ability to be serialized, but as time goes on and the objects are updated, nonserializable stuff creeps in.