EJB 3 In Action - Putting EJB into action - Packaging EJB 3 applications

117 阅读8分钟

Packaging your applications

Modules supported by an EE server
DescriptionDescriptorContents
EJB Java Archive (EJB-JAR)META-INF/ejb-jar.xml or WEB-INF/ejb-jar.xmlSession and message-driven beans. Optionally JPA and CDI may be included as well.
Web Application Archive (WAR)WEB-INF/web.xmlWeb 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.xmlOther Java EE modules such as EJB-JARs and WARs.
Resource Adapter Archive (RAR)META-INF/ra.xmlResource adapters.
Client Application Archives (CAR)META-INF/ application-client.xmlStandalone Java client for EJBs.
Java Persistence Archive (JPA)META-INF/persistence.xml or WEB-INF/persistence.xmlJava 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.xmlJava 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.xml look something like the following listing.

image.png

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.

image.png

Exploring class loading

Class-loading basics

image.png

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.

image.png

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

image.png

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.

image.png

POM file to build an EJB-JAR

image.png

image.png

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:

image.png

image.png

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.

image.png

One-to-one mapping between annotations and XML descriptor elements
AnnotationTypeAnnotation elementCorresponding descriptor element
@StatelessEJB type <session-type>Stateless
  nameejb-name
@StatefulEJB type <session-type>Stateful
  nameejb-name
@MessageDrivenEJB type message-driven
  nameejb-name
@RemoteInterface type remote
@LocalInterface type Local
@TransactionManagementTransaction management type at bean level transaction-type
@TransactionAttributeTransaction settings method container-transaction trans-attribute
@InterceptorsInterceptors interceptor-binding interceptor-class
@ExcludeClassInterceptorsInterceptors exclude-classinterceptor
@ExcludeDefaultInterceptorsInterceptors exclude-defaultinterceptors
@AroundInvokeCustom interceptor around-invoke
@PostActivateLifecycle method post-activate
@PrePassivateLifecycle method pre-passivate
@DeclareRolesSecurity setting security-role
@RolesAllowedSecurity setting method-permission
@PermitAllSecurity setting unchecked
@DenyAllSecurity setting exclude-list
@RunAsSecurity setting security-identity run-as
@ResourceResource references (DataSource, JMS, environment, mail, and so on) resource-ref resource-env-ref message-destination-ref env-ref
 Resource injectionSetter/field injectioninjection-target
@EJBEJB references ejb-ref ejb-local-ref
@Persistence-ContextPersistence context reference persistence-context-ref
@PersistenceUnitPersistence 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: image.png

Spcifying defaul interceptors

image.png

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

image.png

EJB-JAR

image.png

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.

image.png

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

image.png

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 their META-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

image.png

EJB-JAR

image.png

WAR

image.png

Using the bean-discovery-mode annotation

Values for the bean-discovery-mode annotation
ValueDescription
ALLAll types are processed. This behavior is the same as including the beans.xml in a Java EE 6 application.
ANNOTATEDOnly types with bean-defining annotations are processed. This is the default Java EE 7 behavior.
NONEAll 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.

image.png

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 use Thread.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.