Part IV explores Enterprise JavaBeans components. This part contains the following chapters:
Enterprise beans are Java EE components that implement Enterprise JavaBeans (EJB) technology. Enterprise beans run in the EJB container, a runtime environment within the GlassFish Server (see Container Types). Although transparent to the application developer, the EJB container provides system-level services, such as transactions and security, to its enterprise beans. These services enable you to quickly build and deploy enterprise beans, which form the core of transactional Java EE applications.
The following topics are addressed here:
Written in the Java programming language, an enterprise bean is a server-side component that encapsulates the business logic of an application. The business logic is the code that fulfills the purpose of the application. In an inventory control application, for example, the enterprise beans might implement the business logic in methods called checkInventoryLevel and orderProduct. By invoking these 32–bit methods, clients can access the inventory services provided by the application.
For several reasons, enterprise beans simplify the development of large, distributed applications. First, because the EJB container provides system-level services to enterprise beans, the bean developer can concentrate on solving business problems. The EJB container, rather than the bean developer, is responsible for system-level services, such as transaction management and security authorization.
Second, because the beans rather than the clients contain the application’s business logic, the client developer can focus on the presentation of the client. The client developer does not have to code the routines that implement business rules or access databases. As a result, the clients are thinner, a benefit that is particularly important for clients that run on small devices.
Third, because enterprise beans are portable components, the application assembler can build new applications from existing beans. Provided that they use the standard APIs, these applications can run on any compliant Java EE server.
You should consider using enterprise beans if your application has any of the following requirements.
The application must be scalable. To accommodate a growing number of users, you may need to distribute an application’s components across multiple machines. Not only can the enterprise beans of an application run on different machines, but also their location will remain transparent to the clients.
Transactions must ensure data integrity. Enterprise beans support transactions, the mechanisms that manage the concurrent access of shared objects.
The application will have a variety of clients. With only a few lines of code, remote clients can easily locate enterprise beans. These clients can be thin, various, and numerous.
Table 14–1 summarizes the two types of enterprise beans. The following sections discuss each type in more detail.
Table 14–1 Enterprise Bean Types
A session bean encapsulates business logic that can be invoked programmatically by a client over local, remote, or web service client views. To access an application that is deployed on the server, the client invokes the session bean’s methods. The session bean performs work for its client, shielding it from complexity by executing business tasks inside the server.
A session bean is not persistent. (That is, its data is not saved to a database.)
For code samples, see Chapter 16, Running the Enterprise Bean Examples.
Session beans are of three types: stateful, stateless, and singleton.
The state of an object consists of the values of its instance variables. In a stateful session bean, the instance variables represent the state of a unique client/bean session. Because the client interacts (“talks”) with its bean, this state is often called the conversational state.
As its name suggests, a session bean is similar to an interactive session. A session bean is not shared; it can have only one client, in the same way that an interactive session can have only one user. When the client terminates, its session bean appears to terminate and is no longer associated with the client.
The state is retained for the duration of the client/bean session. If the client removes the bean, the session ends and the state disappears. This transient nature of the state is not a problem, however, because when the conversation between the client and the bean ends, there is no need to retain the state.
A stateless session bean does not maintain a conversational state with the client. When a client invokes the methods of a stateless bean, the bean’s instance variables may contain a state specific to that client but only for the duration of the invocation. When the method is finished, the client-specific state should not be retained. Clients may, however, change the state of instance variables in pooled stateless beans, and this state is held over to the next invocation of the pooled stateless bean. Except during method invocation, all instances of a stateless bean are equivalent, allowing the EJB container to assign an instance to any client. That is, the state of a stateless session bean should apply across all clients.
Because they can support multiple clients, stateless session beans can offer better scalability for applications that require large numbers of clients. Typically, an application requires fewer stateless session beans than stateful session beans to support the same number of clients.
A stateless session bean can implement a web service, but a stateful session bean cannot.
A singleton session bean is instantiated once per application and exists for the lifecycle of the application. Singleton session beans are designed for circumstances in which a single enterprise bean instance is shared across and concurrently accessed by clients.
Singleton session beans offer similar functionality to stateless session beans but differ from them in that there is only one singleton session bean per application, as opposed to a pool of stateless session beans, any of which may respond to a client request. Like stateless session beans, singleton session beans can implement web service endpoints.
Singleton session beans maintain their state between client invocations but are not required to maintain their state across server crashes or shutdowns.
Applications that use a singleton session bean may specify that the singleton should be instantiated upon application startup, which allows the singleton to perform initialization tasks for the application. The singleton may perform cleanup tasks on application shutdown as well, because the singleton will operate throughout the lifecycle of the application.
Stateful session beans are appropriate if any of the following conditions are true.
The bean’s state represents the interaction between the bean and a specific client.
The bean needs to hold information about the client across method invocations.
The bean mediates between the client and the other components of the application, presenting a simplified view to the client.
Behind the scenes, the bean manages the work flow of several enterprise beans.
To improve performance, you might choose a stateless session bean if it has any of these traits.
The bean’s state has no data for a specific client.
In a single method invocation, the bean performs a generic task for all clients. For example, you might use a stateless session bean to send an email that confirms an online order.
The bean implements a web service.
Singleton session beans are appropriate in the following circumstances.
State needs to be shared across the application.
A single enterprise bean needs to be accessed by multiple threads concurrently.
The application needs an enterprise bean to perform tasks upon application startup and shutdown.
The bean implements a web service.
A message-driven bean is an enterprise bean that allows Java EE applications to process messages asynchronously. This type of bean normally acts as a JMS message listener, which is similar to an event listener but receives JMS messages instead of events. The messages can be sent by any Java EE component (an application client, another enterprise bean, or a web component) or by a JMS application or system that does not use Java EE technology. Message-driven beans can process JMS messages or other kinds of messages.
The most visible difference between message-driven beans and session beans is that clients do not access message-driven beans through interfaces. Interfaces are described in the section Accessing Enterprise Beans. Unlike a session bean, a message-driven bean has only a bean class.
In several respects, a message-driven bean resembles a stateless session bean.
A message-driven bean’s instances retain no data or conversational state for a specific client.
All instances of a message-driven bean are equivalent, allowing the EJB container to assign a message to any message-driven bean instance. The container can pool these instances to allow streams of messages to be processed concurrently.
A single message-driven bean can process messages from multiple clients.
The instance variables of the message-driven bean instance can contain some state across the handling of client messages, such as a JMS API connection, an open database connection, or an object reference to an enterprise bean object.
Client components do not locate message-driven beans and invoke methods directly on them. Instead, a client accesses a message-driven bean through, for example, JMS by sending messages to the message destination for which the message-driven bean class is the MessageListener. You assign a message-driven bean’s destination during deployment by using GlassFish Server resources.
Message-driven beans have the following characteristics.
They execute upon receipt of a single client message.
They are invoked asynchronously.
They are relatively short-lived.
They do not represent directly shared data in the database, but they can access and update this data.
They can be transaction-aware.
They are stateless.
When a message arrives, the container calls the message-driven bean’s onMessage method to process the message. The onMessage method normally casts the message to one of the five JMS message types and handles it in accordance with the application’s business logic. The onMessage method can call helper methods or can invoke a session bean to process the information in the message or to store it in a database.
A message can be delivered to a message-driven bean within a transaction context, so all operations within the onMessage method are part of a single transaction. If message processing is rolled back, the message will be redelivered. For more information, see Chapter 28, Transactions.
Session beans allow you to send JMS messages and to receive them synchronously but not asynchronously. To avoid tying up server resources, do not to use blocking synchronous receives in a server-side component; in general, JMS messages should not be sent or received synchronously. To receive messages asynchronously, use a message-driven bean.
The material in this section applies only to session beans and not to message-driven beans. Because they have a different programming model, message-driven beans do not have interfaces or no-interface views that define client access.
Clients access enterprise beans either through a no-interface view or through a business interface. A no-interface view of an enterprise bean exposes the public methods of the enterprise bean implementation class to clients. Clients using the no-interface view of an enterprise bean may invoke any public methods in the enterprise bean implementation class or any superclasses of the implementation class. A business interface is a standard Java programming language interface that contains the business methods of the enterprise bean.
A client can access a session bean only through the methods defined in the bean’s business interface or through the public methods of an enterprise bean that has a no-interface view. The business interface or no-interface view defines the client’s view of an enterprise bean. All other aspects of the enterprise bean (method implementations and deployment settings) are hidden from the client.
Well-designed interfaces and no-interface views simplify the development and maintenance of Java EE applications. Not only do clean interfaces and no-interface views shield the clients from any complexities in the EJB tier, but they also allow the enterprise beans to change internally without affecting the clients. For example, if you change the implementation of a session bean business method, you won’t have to alter the client code. But if you were to change the method definitions in the interfaces, you might have to modify the client code as well. Therefore, it is important that you design the interfaces and no-interface views carefully to isolate your clients from possible changes in the enterprise beans.
Session beans can have more than one business interface. Session beans should, but are not required to, implement their business interface or interfaces.
The client of an enterprise bean obtains a reference to an instance of an enterprise bean through either dependency injection, using Java programming language annotations, or JNDI lookup, using the Java Naming and Directory Interface syntax to find the enterprise bean instance.
Dependency injection is the simplest way of obtaining an enterprise bean reference. Clients that run within a Java EE server-managed environment, JavaServer Faces web applications, JAX-RS web services, other enterprise beans, or Java EE application clients, support dependency injection using the javax.ejb.EJB annotation.
Applications that run outside a Java EE server-managed environment, such as Java SE applications, must perform an explicit lookup. JNDI supports a global syntax for identifying Java EE components to simplify this explicit lookup.
Three JNDI namespaces are used for portable JNDI lookups: java:global, java:module, and java:app.
The java:global JNDI namespace is the portable way of finding remote enterprise beans using JNDI lookups. JNDI addresses are of the following form:
java:global[/application name]/module name/enterprise bean name[/interface name] |
Application name and module name default to the name of the application and module minus the file extension. Application names are required only if the application is packaged within an EAR. The interface name is required only if the enterprise bean implements more than one business interface.
The java:module namespace is used to look up local enterprise beans within the same module. JNDI addresses using the java:module namespace are of the following form:
java:module/enterprise bean name/[interface name] |
The interface name is required only if the enterprise bean implements more than one business interface.
The java:app namespace is used to look up local enterprise beans packaged within the same application. That is, the enterprise bean is packaged within an EAR file containing multiple Java EE modules. JNDI addresses using the java:app namespace are of the following form:
java:app[/module name]/enterprise bean name[/interface name] |
The module name is optional. The interface name is required only if the enterprise bean implements more than one business interface.
For example, if an enterprise bean, MyBean, is packaged within the web application archive myApp.war, the module name is myApp. The portable JNDI name is java:module/MyBean An equivalent JNDI name using the java:global namespace is java:global/myApp/MyBean.
When you design a Java EE application, one of the first decisions you make is the type of client access allowed by the enterprise beans: remote, local, or web service.
Whether to allow local or remote access depends on the following factors.
Tight or loose coupling of related beans: Tightly coupled beans depend on one another. For example, if a session bean that processes sales orders calls a session bean that emails a confirmation message to the customer, these beans are tightly coupled. Tightly coupled beans are good candidates for local access. Because they fit together as a logical unit, they typically call each other often and would benefit from the increased performance that is possible with local access.
Type of client: If an enterprise bean is accessed by application clients, it should allow remote access. In a production environment, these clients almost always run on machines other than those on which the GlassFish Server is running. If an enterprise bean’s clients are web components or other enterprise beans, the type of access depends on how you want to distribute your components.
Component distribution: Java EE applications are scalable because their server-side components can be distributed across multiple machines. In a distributed application, for example, the server that the web components run on may not be the one on which the enterprise beans they access are deployed. In this distributed scenario, the enterprise beans should allow remote access.
Performance: Owing to such factors as network latency, remote calls may be slower than local calls. On the other hand, if you distribute components among different servers, you may improve the application’s overall performance. Both of these statements are generalizations; performance can vary in different operational environments. Nevertheless, you should keep in mind how your application design might affect performance.
If you aren’t sure which type of access an enterprise bean should have, choose remote access. This decision gives you more flexibility. In the future, you can distribute your components to accommodate the growing demands on your application.
Although it is uncommon, it is possible for an enterprise bean to allow both remote and local access. If this is the case, either the business interface of the bean must be explicitly designated as a business interface by being decorated with the @Remote or @Local annotations, or the bean class must explicitly designate the business interfaces by using the @Remote and @Local annotations. The same business interface cannot be both a local and a remote business interface.
A local client has these characteristics.
It must run in the same application as the enterprise bean it accesses.
It can be a web component or another enterprise bean.
To the local client, the location of the enterprise bean it accesses is not transparent.
The no-interface view of an enterprise bean is a local view. The public methods of the enterprise bean implementation class are exposed to local clients that access the no-interface view of the enterprise bean. Enterprise beans that use the no-interface view do not implement a business interface.
The local business interface defines the bean’s business and lifecycle methods. If the bean’s business interface is not decorated with @Local or @Remote, and if the bean class does not specify the interface using @Local or @Remote, the business interface is by default a local interface.
To build an enterprise bean that allows only local access, you may, but are not required to, do one of the following:
Create an enterprise bean implementation class that does not implement a business interface, indicating that the bean exposes a no-interface view to clients. For example:
@Session public class MyBean { ... }
Annotate the business interface of the enterprise bean as a @Local interface. For example:
@Local public interface InterfaceName { ... }
Specify the interface by decorating the bean class with @Local and specify the interface name. For example:
@Local(InterfaceName.class) public class BeanName implements InterfaceName { ... }
Client access to an enterprise bean that exposes a local, no-interface view is accomplished through either dependency injection or JNDI lookup.
To obtain a reference to the no-interface view of an enterprise bean through dependency injection, use the javax.ejb.EJB annotation and specify the enterprise bean’s implementation class:
@EJB ExampleBean exampleBean;
To obtain a reference to the no-interface view of an enterprise bean through JNDI lookup, use the javax.naming.InitialContext interface’s lookup method:
ExampleBean exampleBean = (ExampleBean) InitialContext.lookup("java:module/ExampleBean");
Clients do not use the new operator to obtain a new instance of an enterprise bean that uses a no-interface view.
Client access to enterprise beans that implement local business interfaces is accomplished through either dependency injection or JNDI lookup.
To obtain a reference to the local business interface of an enterprise bean through dependency injection, use the javax.ejb.EJB annotation and specify the enterprise bean’s local business interface name:
@EJB Example example;
To obtain a reference to a local business interface of an enterprise bean through JNDI lookup, use the javax.naming.InitialContext interface’s lookup method:
ExampleLocal example = (ExampleLocal) InitialContext.lookup("java:module/ExampleLocal");
A remote client of an enterprise bean has the following traits.
It can run on a different machine and a different JVM from the enterprise bean it accesses. (It is not required to run on a different JVM.)
It can be a web component, an application client, or another enterprise bean.
To a remote client, the location of the enterprise bean is transparent.
The enterprise bean must implement a business interface. That is, remote clients may not access an enterprise bean through a no-interface view.
To create an enterprise bean that allows remote access, you must either
Decorate the business interface of the enterprise bean with the @Remote annotation:
@Remote public interface InterfaceName { ... }
Decorate the bean class with @Remote, specifying the business interface or interfaces:
@Remote(InterfaceName.class) public class BeanName implements InterfaceName { ... }
The remote interface defines the business and lifecycle methods that are specific to the bean. For example, the remote interface of a bean named BankAccountBean might have business methods named deposit and credit. Figure 14–1 shows how the interface controls the client’s view of an enterprise bean.
Client access to an enterprise bean that implements a remote business interface is accomplished through either dependency injection or JNDI lookup.
To obtain a reference to the remote business interface of an enterprise bean through dependency injection, use the javax.ejb.EJB annotation and specify the enterprise bean’s remote business interface name:
@EJB Example example;
To obtain a reference to a remote business interface of an enterprise bean through JNDI lookup, use the javax.naming.InitialContext interface’s lookup method:
ExampleRemote example = (ExampleRemote) InitialContext.lookup("java:global/myApp/ExampleRemote");
A web service client can access a Java EE application in two ways. First, the client can access a web service created with JAX-WS. (For more information on JAX-WS, see Chapter 12, Building Web Services with JAX-WS.) Second, a web service client can invoke the business methods of a stateless session bean. Message beans cannot be accessed by web service clients.
Provided that it uses the correct protocols (SOAP, HTTP, WSDL), any web service client can access a stateless session bean, whether or not the client is written in the Java programming language. The client doesn’t even “know” what technology implements the service: stateless session bean, JAX-WS, or some other technology. In addition, enterprise beans and web components can be clients of web services. This flexibility enables you to integrate Java EE applications with web services.
A web service client accesses a stateless session bean through the bean’s web service endpoint implementation class. By default, all public methods in the bean class are accessible to web service clients. The @WebMethod annotation may be used to customize the behavior of web service methods. If the @WebMethod annotation is used to decorate the bean class’s methods, only those methods decorated with @WebMethod are exposed to web service clients.
For a code sample, see A Web Service Example: helloservice.
The type of access affects the parameters of the bean methods that are called by clients. The following sections apply not only to method parameters but also to method return values.
The parameters of remote calls are more isolated than those of local calls. With remote calls, the client and the bean operate on different copies of a parameter object. If the client changes the value of the object, the value of the copy in the bean does not change. This layer of isolation can help protect the bean if the client accidentally modifies the data.
In a local call, both the client and the bean can modify the same parameter object. In general, you should not rely on this side effect of local calls. Perhaps someday you will want to distribute your components, replacing the local calls with remote ones.
As with remote clients, web service clients operate on different copies of parameters than does the bean that implements the web service.
Because remote calls are likely to be slower than local calls, the parameters in remote methods should be relatively coarse-grained. A coarse-grained object contains more data than a fine-grained one, so fewer access calls are required. For the same reason, the parameters of the methods called by web service clients should also be coarse-grained.
To develop an enterprise bean, you must provide the following files:
Enterprise bean class: Implements the business methods of the enterprise bean and any lifecycle callback methods.
Business interfaces: Define the business methods implemented by the enterprise bean class. A business interface is not required if the enterprise bean exposes a local, no-interface view.
Helper classes: Other classes needed by the enterprise bean class, such as exception and utility classes.
Package the programming artifacts in the preceding list either into an EJB JAR file (a stand-alone module that stores the enterprise bean) or within a web application archive (WAR) module.
An EJB JAR file is portable and can be used for various applications.
To assemble a Java EE application, package one or more modules, such as EJB JAR files, into an EAR file, the archive file that holds the application. When deploying the EAR file that contains the enterprise bean’s EJB JAR file, you also deploy the enterprise bean to the GlassFish Server. You can also deploy an EJB JAR that is not contained in an EAR file. Figure 14–2 shows the contents of an EJB JAR file.
Enterprise beans often provide the business logic of a web application. In these cases, packaging the enterprise bean within the web application’s WAR module simplifies deployment and application organization. Enterprise beans may be packaged within a WAR module as Java programming language class files or within a JAR file that is bundled within the WAR module.
To include enterprise bean class files in a WAR module, the class files should be in the WEB-INF/classes directory.
To include a JAR file that contains enterprise beans in a WAR module, add the JAR to the WEB-INF/lib directory of the WAR module.
WAR modules that contain enterprise beans do not require an ejb-jar.xml deployment descriptor. If the application uses ejb-jar.xml, it must be located in the WAR module’s WEB-INF directory.
JAR files that contain enterprise bean classes packaged within a WAR module are not considered EJB JAR files, even if the bundled JAR file conforms to the format of an EJB JAR file. The enterprise beans contained within the JAR file are semantically equivalent to enterprise beans located in the WAR module’s WEB-INF/classes directory, and the environment namespace of all the enterprise beans are scoped to the WAR module.
For example, suppose that a web application consists of a shopping cart enterprise bean, a credit card processing enterprise bean, and a Java servlet front end. The shopping cart bean exposes a local, no-interface view and is defined as follows:
package com.example.cart; @Stateless public class CartBean { ... }
The credit card processing bean is packaged within its own JAR file, cc.jar, exposes a local, no-interface view, and is defined as follows:
package com.example.cc; @Stateless public class CreditCardBean { ... }
The servlet, com.example.web.StoreServlet, handles the web front end and uses both CartBean and CreditCardBean. The WAR module layout for this application looks as follows:
WEB-INF/classes/com/example/cart/CartBean.class WEB-INF/classes/com/example/web/StoreServlet WEB-INF/lib/cc.jar WEB-INF/ejb-jar.xml WEB-INF/web.xml
Because enterprise beans are composed of multiple parts, it’s useful to follow a naming convention for your applications. Table 14–2 summarizes the conventions for the example beans in this tutorial.
Table 14–2 Naming Conventions for Enterprise Beans
Item |
Syntax |
Example |
---|---|---|
Enterprise bean name |
nameBean |
AccountBean |
Enterprise bean class |
nameBean |
AccountBean |
Business interface |
name |
Account |
An enterprise bean goes through various stages during its lifetime, or lifecycle. Each type of enterprise bean (stateful session, stateless session, singleton session, or message-driven) has a different lifecycle.
The descriptions that follow refer to methods that are explained along with the code examples in the next two chapters. If you are new to enterprise beans, you should skip this section and run the code examples first.
Figure 14–3 illustrates the stages that a session bean passes through during its lifetime. The client initiates the lifecycle by obtaining a reference to a stateful session bean. The container performs any dependency injection and then invokes the method annotated with @PostConstruct, if any. The bean is now ready to have its business methods invoked by the client.
While in the ready stage, the EJB container may decide to deactivate, or passivate, the bean by moving it from memory to secondary storage. (Typically, the EJB container uses a least-recently-used algorithm to select a bean for passivation.) The EJB container invokes the method annotated @PrePassivate, if any, immediately before passivating it. If a client invokes a business method on the bean while it is in the passive stage, the EJB container activates the bean, calls the method annotated @PostActivate, if any, and then moves it to the ready stage.
At the end of the lifecycle, the client invokes a method annotated @Remove, and the EJB container calls the method annotated @PreDestroy, if any. The bean’s instance is then ready for garbage collection.
Your code controls the invocation of only one lifecycle method: the method annotated @Remove. All other methods in Figure 14–3 are invoked by the EJB container. See Chapter 29, Resource Connections for more information.
Because a stateless session bean is never passivated, its lifecycle has only two stages: nonexistent and ready for the invocation of business methods. Figure 14–4 illustrates the stages of a stateless session bean.
The EJB container typically creates and maintains a pool of stateless session beans, beginning the stateless session bean’s lifecycle. The container performs any dependency injection and then invokes the method annotated @PostConstruct, if it exists. The bean is now ready to have its business methods invoked by a client.
At the end of the lifecycle, the EJB container calls the method annotated @PreDestroy, if it exists. The bean’s instance is then ready for garbage collection.
Like a stateless session bean, a singleton session bean is never passivated and has only two stages, nonexistent and ready for the invocation of business methods, as shown in Figure 14–5.
The EJB container initiates the singleton session bean lifecycle by creating the singleton instance. This occurs upon application deployment if the singleton is annotated with the @Startup annotation The container performs any dependency injection and then invokes the method annotated @PostConstruct, if it exists. The singleton session bean is now ready to have its business methods invoked by the client.
At the end of the lifecycle, the EJB container calls the method annotated @PreDestroy, if it exists. The singleton session bean is now ready for garbage collection.
Figure 14–6 illustrates the stages in the lifecycle of a message-driven bean.
The EJB container usually creates a pool of message-driven bean instances. For each instance, the EJB container performs these tasks.
If the message-driven bean uses dependency injection, the container injects these references before instantiating the instance.
The container calls the method annotated @PostConstruct, if any.
Like a stateless session bean, a message-driven bean is never passivated and has only two states: nonexistent and ready to receive messages.
At the end of the lifecycle, the container calls the method annotated @PreDestroy, if any. The bean’s instance is then ready for garbage collection.
For more information on Enterprise JavaBeans technology, see
Enterprise JavaBeans 3.1 specification:
Enterprise JavaBeans web site:
This chapter shows how to develop, deploy, and run a simple Java EE application named converter. The purpose of converter is to calculate currency conversions between Japanese yen and Eurodollars. The converter application consists of an enterprise bean, which performs the calculations, and two types of clients: an application client and a web client.
Here’s an overview of the steps you’ll follow in this chapter:
Create the enterprise bean: ConverterBean.
Create the web client.
Deploy converter onto the server.
Using a browser, run the web client.
Before proceeding, make sure that you’ve done the following:
Read Chapter 1, Overview
Become familiar with enterprise beans (see Chapter 14, Enterprise Beans)
Started the server (see Starting and Stopping the GlassFish Server)
The following topics are addressed here:
The enterprise bean in our example is a stateless session bean called ConverterBean. The source code for ConverterBean is in the tut-install/examples/ejb/converter/src/java/ directory.
Creating ConverterBean requires these steps:
Coding the bean’s implementation class (the source code is provided)
Compiling the source code
The enterprise bean class for this example is called ConverterBean. This class implements two business methods: dollarToYen and yenToEuro. Because the enterprise bean class doesn’t implement a business interface, the enterprise bean exposes a local, no-interface view. The public methods in the enterprise bean class are available to clients that obtain a reference to ConverterBean. The source code for the ConverterBean class is as follows:
package com.sun.tutorial.javaee.ejb; import java.math.BigDecimal; import javax.ejb.*; @Stateless public class ConverterBean { private BigDecimal yenRate = new BigDecimal("115.3100"); private BigDecimal euroRate = new BigDecimal("0.0071"); public BigDecimal dollarToYen(BigDecimal dollars) { BigDecimal result = dollars.multiply(yenRate); return result.setScale(2, BigDecimal.ROUND_UP); } public BigDecimal yenToEuro(BigDecimal yen) { BigDecimal result = yen.multiply(euroRate); return result.setScale(2, BigDecimal.ROUND_UP); } }
Note the @Stateless annotation decorating the enterprise bean class. This annotation lets the container know that ConverterBean is a stateless session bean.
The web client is contained in the following servlet class:
tut-install/examples/ejb/converter/src/java/converter/web/ConverterServlet.java
A Java servlet is a web component that responds to HTTP requests.
The ConverterServlet class uses dependency injection to obtain a reference to ConverterBean. The javax.ejb.EJB annotation is added to the declaration of the private member variable converterBean, which is of type ConverterBean. ConverterBean exposes a local, no-interface view, so the enterprise bean implementation class is the variable type:
@WebServlet public class ConverterServlet extends HttpServlet { @EJB ConverterBean converterBean; ... }
When the user enters an amount to be converted to yen and euro, the amount is retrieved from the request parameters; then the ConverterBean.dollarToYen and the ConverterBean.yenToEuro methods are called:
... try { String amount = request.getParameter("amount"); if (amount != null && amount.length() > 0) { // convert the amount to a BigDecimal from the request parameter BigDecimal d = new BigDecimal(amount); // call the ConverterBean.dollarToYen() method to get the amount // in Yen BigDecimal yenAmount = converter.dollarToYen(d); // call the ConverterBean.yenToEuro() method to get the amount // in Euros BigDecimal euroAmount = converter.yenToEuro(yenAmount); ... } ... }
The results are displayed to the user.
Now you are ready to compile the enterprise bean class (ConverterBean.java) and the servlet class (ConverterServlet.java) and to package the compiled classes into a WAR file.
In NetBeans IDE, select File->Open Project.
In the Open Project dialog, navigate to:
tut-install/examples/ejb/ |
Select the converter folder.
Select the Open as Main Project and Open Required Projects check boxes.
Click Open Project.
In the Projects tab, right-click the converter project and select Deploy.
A web browser window opens the URL http://localhost:8080/converter.
In a terminal window, go to:
tut-install/examples/ejb/converter/ |
Type the following command:
ant all |
This command calls the default task, which compiles the source files for the enterprise bean and the servlet, placing the class files in the build subdirectory (not the src directory) of the project. The default task packages the project into a WAR module: converter.war. For more information about the Ant tool, see Building the Examples.
When compiling the code, the ant task includes the Java EE API JAR files in the classpath. These JARs reside in the modules directory of your GlassFish Server installation. If you plan to use other tools to compile the source code for Java EE components, make sure that the classpath includes the Java EE API JAR files.
Open a web browser to the following URL:
http://localhost:8080/converter |
The screen shown in Figure 15–1 appears.
Type 100 in the input field and click Submit.
A second page appears, showing the converted values.
The GlassFish Server supports iterative development. Whenever you make a change to a Java EE application, you must redeploy the application.
To modify a class file in an enterprise bean, you change the source code, recompile it, and redeploy the application. For example, if you want to change the exchange rate in the dollarToYen business method of the ConverterBean class, you would follow these steps.
To modify ConverterServlet, the procedure is the same.
Edit ConverterBean.java and save the file.
Recompile the source file.
Session beans provide a simple but powerful way to encapsulate business logic within an application. They can be accessed from remote Java clients, web service clients, and components running in the same server.
In Chapter 15, Getting Started with Enterprise Beans, you built a stateless session bean named ConverterBean. This chapter examines the source code of four more session beans:
CartBean: a stateful session bean that is accessed by a remote client
CounterBean: a singleton session bean
HelloServiceBean: a stateless session bean that implements a web service
TimerSessionBean: a stateless session bean that sets a timer
The following topics are addressed here:
The cart example represents a shopping cart in an online bookstore and uses a stateful session bean to manage the operations of the shopping cart. The bean’s client can add a book to the cart, remove a book, or retrieve the cart’s contents. To assemble cart, you need the following code:
Session bean class (CartBean)
Remote business interface (Cart)
All session beans require a session bean class. All enterprise beans that permit remote access must have a remote business interface. To meet the needs of a specific application, an enterprise bean may also need some helper classes. The CartBean session bean uses two helper classes, BookException and IdVerifier, which are discussed in the section Helper Classes.
The source code for this example is in the tut-install/examples/ejb/cart/ directory.
The Cart business interface is a plain Java interface that defines all the business methods implemented in the bean class. If the bean class implements a single interface, that interface is assumed to the business interface. The business interface is a local interface unless it is annotated with the javax.ejb.Remote annotation; the javax.ejb.Local annotation is optional in this case.
The bean class may implement more than one interface. In that case, the business interfaces must either be explicitly annotated @Local or @Remote or be specified by decorating the bean class with @Local or @Remote. However, the following interfaces are excluded when determining whether the bean class implements more than one interface:
java.io.Serializable
java.io.Externalizable
Any of the interfaces defined by the javax.ejb package
The source code for the Cart business interface follows:
package com.sun.tutorial.javaee.ejb; import java.util.List; import javax.ejb.Remote; @Remote public interface Cart { public void initialize(String person) throws BookException; public void initialize(String person, String id) throws BookException; public void addBook(String title); public void removeBook(String title) throws BookException; public List<String> getContents(); public void remove(); }
The session bean class for this example is called CartBean. Like any stateful session bean, the CartBean class must meet the following requirements.
The class implements the business methods defined in the business interface.
Stateful session beans also may
Implement the business interface, a plain Java interface. It is good practice to implement the bean’s business interface.
Implement any optional lifecycle callback methods, annotated @PostConstruct, @PreDestroy, @PostActivate, and @PrePassivate.
The source code for the CartBean class follows:
package com.sun.tutorial.javaee.ejb; import java.util.ArrayList; import java.util.List; import javax.ejb.Remove; import javax.ejb.Stateful; @Stateful public class CartBean implements Cart { String customerName; String customerId; List<String> contents; public void initialize(String person) throws BookException { if (person == null) { throw new BookException("Null person not allowed."); } else { customerName = person; } customerId = "0"; contents = new ArrayList<String>(); } public void initialize(String person, String id) throws BookException { if (person == null) { throw new BookException("Null person not allowed."); } else { customerName = person; } IdVerifier idChecker = new IdVerifier(); if (idChecker.validate(id)) { customerId = id; } else { throw new BookException("Invalid id: " + id); } contents = new ArrayList<String>(); } public void addBook(String title) { contents.add(title); } public void removeBook(String title) throws BookException { boolean result = contents.remove(title); if (result == false) { throw new BookException(title + " not in cart."); } } public List<String> getContents() { return contents; } @Remove public void remove() { contents = null; } }
A method in the bean class may be declared as a lifecycle callback method by annotating the method with the following annotations:
javax.annotation.PostConstruct: Methods annotated with @PostConstruct are invoked by the container on newly constructed bean instances after all dependency injection has completed and before the first business method is invoked on the enterprise bean.
javax.annotation.PreDestroy: Methods annotated with @PreDestroy are invoked after any method annotated @Remove has completed and before the container removes the enterprise bean instance.
javax.ejb.PostActivate: Methods annotated with @PostActivate are invoked by the container after the container moves the bean from secondary storage to active status.
javax.ejb.PrePassivate: Methods annotated with @PrePassivate are invoked by the container before it passivates the enterprise bean, meaning that the container temporarily removes the bean from the environment and saves it to secondary storage.
Lifecycle callback methods must return void and have no parameters.
The primary purpose of a session bean is to run business tasks for the client. The client invokes business methods on the object reference it gets from dependency injection or JNDI lookup. From the client’s perspective, the business methods appear to run locally, although they run remotely in the session bean. The following code snippet shows how the CartClient program invokes the business methods:
cart.create("Duke DeEarl", "123"); ... cart.addBook("Bel Canto"); ... List<String> bookList = cart.getContents(); ... cart.removeBook("Gravity’s Rainbow");
The CartBean class implements the business methods in the following code:
public void addBook(String title) { contents.addElement(title); } public void removeBook(String title) throws BookException { boolean result = contents.remove(title); if (result == false) { throw new BookException(title + "not in cart."); } } public List<String> getContents() { return contents; }
The signature of a business method must conform to these rules.
The method name must not begin with ejb, to avoid conflicts with callback methods defined by the EJB architecture. For example, you cannot call a business method ejbCreate or ejbActivate.
The access control modifier must be public.
If the bean allows remote access through a remote business interface, the arguments and return types must be legal types for the Java Remote Method Invocation (RMI) API.
If the bean is a web service endpoint, the arguments and return types for the methods annotated @WebMethod must be legal types for JAX-WS.
The modifier must not be static or final.
The throws clause can include exceptions that you define for your application. The removeBook method, for example, throws a BookException if the book is not in the cart.
To indicate a system-level problem, such as the inability to connect to a database, a business method should throw a javax.ejb.EJBException. The container will not wrap application exceptions, such as BookException. Because EJBException is a subclass of RuntimeException, you do not need to include it in the throws clause of the business method.
Business methods annotated with javax.ejb.Remove in the stateful session bean class can be invoked by enterprise bean clients to remove the bean instance. The container will remove the enterprise bean after a @Remove method completes, either normally or abnormally.
In CartBean, the remove method is a @Remove method:
@Remove public void remove() { contents = null; }
The CartBean session bean has two helper classes: BookException and IdVerifier. The BookException is thrown by the removeBook method, and the IdVerifier validates the customerId in one of the create methods. Helper classes may reside in an EJB JAR file that contains the enterprise bean class, a WAR file if the enterprise bean is packaged within a WAR, or in an EAR that contains an EJB JAR or a WAR file that contains an enterprise bean.
Now you are ready to compile the remote interface (Cart.java), the home interface (CartHome.java), the enterprise bean class (CartBean.java), the client class (CartClient.java), and the helper classes (BookException.java and IdVerifier.java). Follow these steps.
You can build, package, deploy, and run the cart application using either NetBeans IDE or the Ant tool.
In NetBeans IDE, select File->Open Project.
In the Open Project dialog, navigate to:
tut-install/examples/ejb/ |
Select the cart folder.
Select the Open as Main Project and Open Required Projects check boxes.
Click Open Project.
In the Projects tab, right-click the cart project and select Deploy.
This builds and packages the application into cart.ear, located in tut-install/examples/ejb/cart/dist/, and deploys this EAR file to your GlassFish Server instance.
To run the cart application client, select Run->Run Main Project.
You will see the output of the application client in the Output pane:
... Retrieving book title from cart: Infinite Jest Retrieving book title from cart: Bel Canto Retrieving book title from cart: Kafka on the Shore Removing "Gravity’s Rainbow" from cart. Caught a BookException: "Gravity’s Rainbow" not in cart. Java Result: 1 run-cart-app-client: run-nb: BUILD SUCCESSFUL (total time: 14 seconds) |
In a terminal window, go to:
tut-install/examples/ejb/cart/ |
Type the following command:
ant |
This command calls the default target, which builds and packages the application into an EAR file, cart.ear, located in the dist directory.
Type the following command:
ant deploy |
The cart.ear file is deployed to the GlassFish Server.
Type the following command:
ant run |
This task retrieves the application client JAR, cartClient.jar, and runs the application client. The client JAR, cartClient.jar, contains the application client class, the helper class BookException, and the Cart business interface.
This task is equivalent to running the following command:
appclient -client cartClient.jar |
When you run the client, the application client container injects any component references declared in the application client class, in this case the reference to the Cart enterprise bean.
As a convenience, the all task will build, package, deploy, and run the application. To do this, enter the following command:
ant all |
The counter example demonstrates how to create a singleton session bean.
The javax.ejb.Singleton annotation is used to specify that the enterprise bean implementation class is a singleton session bean:
@Singleton public class SingletonBean { ... }
The EJB container is responsible for determining when to initialize a singleton session bean instance unless the singleton session bean implementation class is annotated with the javax.ejb.Startup annotation. In this case, sometimes called eager initialization, the EJB container must initialize the singleton session bean upon application startup. The singleton session bean is initialized before the EJB container delivers client requests to any enterprise beans in the application. This allows the singleton session bean to perform, for example, application startup tasks.
The following singleton session bean stores the status of an application and is eagerly initialized:
@Startup @Singleton public class StatusBean { private String status; @PostConstruct void init { status = "Ready"; } ... }
Sometimes multiple singleton session beans are used to initialize data for an application and therefore must be initialized in a specific order. In these cases, use the javax.ejb.DependsOn annotation to declare the startup dependencies of the singleton session bean. The @DependsOn annotation’s value attribute is one or more strings that specify the name of the target singleton session bean. If more than one dependent singleton bean is specified in @DependsOn, the order in which they are listed is not necessarily the order in which the EJB container will initialize the target singleton session beans.
The following singleton session bean, PrimaryBean, should be started up first:
@Singleton public class PrimaryBean { ... }
SecondaryBean depends on PrimaryBean:
@Singleton @DependsOn("PrimaryBean") public class SecondaryBean { ... }
This guarantees that the EJB container will initialize PrimaryBean before SecondaryBean.
The following singleton session bean, TertiaryBean, depends on PrimaryBean and SecondaryBean:
@Singleton @DependsOn("PrimaryBean", "SecondaryBean") public class TertiaryBean { ... }
SecondaryBean explicitly requires PrimaryBean to be initialized before it is initialized, through its own @DependsOn annotation. In this case, the EJB container will first initialize PrimaryBean, then SecondaryBean, and finally TertiaryBean.
If, however, SecondaryBean did not explicitly depend on PrimaryBean, the EJB container may initialize either PrimaryBean or SecondaryBean first. That is, the EJB container could initialize the singletons in the following order: SecondaryBean, PrimaryBean, TertiaryBean.
Singleton session beans are designed for concurrent access, situations in which many clients need to access a single instance of a session bean at the same time. A singleton’s client needs only a reference to a singleton in order to invoke any business methods exposed by the singleton and doesn’t need to worry about any other clients that may be simultaneously invoking business methods on the same singleton.
When creating a singleton session bean, concurrent access to the singleton’s business methods can be controlled in two ways: container-managed concurrency and bean-managed concurrency.
The javax.ejb.ConcurrencyManagement annotation is used to specify container-managed or bean-managed concurrency for the singleton. With @ConcurrencyManagement, a type attribute must be set to either javax.ejb.ConcurrencyManagementType.CONTAINER or javax.ejb.ConcurrencyManagementType.BEAN. If no @ConcurrencyManagement annotation is present on the singleton implementation class, the EJB container default of container-managed concurrency is used.
If a singleton uses container-managed concurrency, the EJB container controls client access to the business methods of the singleton. The javax.ejb.Lock annotation and a javax.ejb.LockType type are used to specify the access level of the singleton’s business methods or @Timeout methods.
Annotate a singleton’s business or timeout method with @Lock(READ) if the method can be concurrently accessed, or shared, with many clients. Annotate the business or timeout method with @Lock(WRITE) if the singleton session bean should be locked to other clients while a client is calling that method. Typically, the @Lock(WRITE) annotation is used when clients are modifying the state of the singleton.
Annotating a singleton class with @Lock specifies that all the business methods and any timeout methods of the singleton will use the specified lock type unless they explicitly set the lock type with a method-level @Lock annotation. If no @Lock annotation is present on the singleton class, the default lock type, @Lock(WRITE), is applied to all business and timeout methods.
The following example shows how to use the @ConcurrencyManagement, @Lock(READ), and @Lock(WRITE) annotations for a singleton that uses container-managed concurrency.
Although by default, singletons use container-managed concurrency, the @ConcurrencyManagement(CONTAINER) annotation may be added at the class level of the singleton to explicitly set the concurrency management type:
@ConcurrencyManagement(CONTAINER) @Singleton public class ExampleSingletonBean { private String state; @Lock(READ) public String getState() { return state; } @Lock(WRITE) public void setState(String newState) { state = newState; } }
The getState method can be accessed by many clients at the same time because it is annotated with @Lock(READ). When the setState method is called, however, all the methods in ExampleSingletonBean will be locked to other clients because setState is annotated with @Lock(WRITE). This prevents two clients from attempting to simultaneously change the state variable of ExampleSingletonBean.
The getData and getStatus methods in the following singleton are of type READ, and the setStatus method is of type WRITE:
@Singleton @Lock(READ) public class SharedSingletonBean { private String data; private String status; public String getData() { return data; } public String getStatus() { return status; } @Lock(WRITE) public void setStatus(String newStatus) { status = newStatus; } }
If a method is of locking type WRITE, client access to all the singleton’s methods is blocked until the current client finishes its method call or an access timeout occurs. When an access timeout occurs, the EJB container throws a javax.ejb.ConcurrentAccessTimeoutException. The javax.ejb.AccessTimeout annotation is used to specify the number of milliseconds before an access timeout occurs. If added at the class level of a singleton, @AccessTimeout specifies the access timeout value for all methods in the singleton unless a method explicitly overrides the default with its own @AccessTimeout annotation.
The @AccessTimeout annotation can be applied to both @Lock(READ) and @Lock(WRITE) methods. The @AccessTimeout annotation has one required element, value, and one optional element, unit. By default, the value is specified in milliseconds. To change the value unit, set unit to one of the java.util.concurrent.TimeUnit constants: NANOSECONDS, MICROSECONDS, MILLISECONDS, or SECONDS.
The following singleton has a default access timeout value of 120,000 milliseconds, or 2 minutes. The doTediousOperation method overrides the default access timeout and sets the value to 360,000 milliseconds, or 6 minutes.
@Singleton @AccessTimeout(value=120000) public class StatusSingletonBean { private String status; @Lock(WRITE) public void setStatus(String new Status) { status = newStatus; } @Lock(WRITE) @AccessTimeout(value=360000) public void doTediousOperation { ... } }
The following singleton has a default access timeout value of 60 seconds, specified using the TimeUnit.SECONDS constant:
@Singleton @AccessTimeout(value=60, timeUnit=SECONDS) public class StatusSingletonBean { ... }
Singletons that use bean-managed concurrency allow full concurrent access to all the business and timeout methods in the singleton. The developer of the singleton is responsible for ensuring that the state of the singleton is synchronized across all clients. Developers who create singletons with bean-managed concurrency are allowed to use the Java programming language synchronization primitives, such as synchronization and volatile, to prevent errors during concurrent access.
Add a @ConcurrencyManagement annotation at the class level of the singleton to specify bean-managed concurrency:
@ConcurrencyManagement(BEAN) @Singleton public class AnotherSingletonBean { ... }
If a singleton session bean encounters an error when initialized by the EJB container, that singleton instance will be destroyed.
Unlike other enterprise beans, once a singleton session bean instance is initialized, it is not destroyed if the singleton’s business or lifecycle methods cause system exceptions. This ensures that the same singleton instance is used throughout the application lifecycle.
The counter example consists of a singleton session bean, CounterBean, and a JavaServer Faces Facelets web front end.
CounterBean is a simple singleton with one method, getHits, that returns an integer representing the number of times a web page has been accessed. Here is the code of CounterBean:
package counter.ejb; import javax.ejb.Singleton; /** * CounterBean is a simple singleton session bean that records the number * of hits to a web page. */ @Singleton public class CounterBean { private int hits = 1; // Increment and return the number of hits public int getHits() { return hits++; } }
The @Singleton annotation marks CounterBean as a singleton session bean. CounterBean uses a local, no-interface view.
CounterBean uses the EJB container’s default metadata values for singletons to simplify the coding of the singleton implementation class. There is no @ConcurrencyManagement annotation on the class, so the default of container-managed concurrency access is applied. There is no @Lock annotation on the class or business method, so the default of @Lock(WRITE) is applied to the only business method, getHits.
The following version of CounterBean is functionally equivalent to the preceding version:
package counter.ejb; import javax.ejb.Singleton; import javax.ejb.ConcurrencyManagement; import static javax.ejb.ConcurrencyManagementType.CONTAINER; import javax.ejb.Lock; import javax.ejb.LockType.WRITE; /** * CounterBean is a simple singleton session bean that records the number * of hits to a web page. */ @Singleton @ConcurrencyManagement(CONTAINER) public class CounterBean { private int hits = 1; // Increment and return the number of hits @Lock(WRITE) public int getHits() { return hits++; } }
The web front end of counter consists of a JavaServer Faces managed bean, Count.java, that is used by the Facelets XHTML files template.xhtml and template-client.xhtml. The Count JavaServer Faces managed bean obtains a reference to CounterBean through dependency injection. Count defines a hitCount JavaBeans property. When the getHitCount getter method is called from the XHTML files, CounterBean's getHits method is called to return the current number of page hits.
Here’s the Count managed bean class:
@ManagedBean @SessionScoped public class Count { @EJB private CounterBean counterBean; private int hitCount; public Count() { this.hitCount = 0; } public int getHitCount() { hitCount = counterBean.getHits(); return hitCount; } public void setHitCount(int newHits) { this.hitCount = newHits; } }
The template.xhtml and template-client.xhtml files are used to render a Facelets view that displays the number of hits to that view. The template-client.xhtml file uses an expression language statement, #{count.hitCount}, to access the hitCount property of the Count managed bean. Here is the content of template-client.xhtml:
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"> <body> This text above will not be displayed. <ui:composition template="/template.xhtml"> This text will not be displayed. <ui:define name="title"> This page has been accessed #{count.hitCount} time(s). </ui:define> This text will also not be displayed. <ui:define name="body"> Hooray! </ui:define> This text will not be displayed. </ui:composition> This text below will also not be displayed. </body> </html>
The counter example application can be built, deployed, and run using NetBeans IDE or Ant.
In NetBeans IDE, select File->Open Project.
In the Open Project dialog, navigate to:
tut-install/examples/ejb/ |
Select the counter folder.
Select the Open as Main Project check box.
Click Open Project.
In the Projects tab, right-click the counter project and select Run.
A web browser will open the URL http://localhost:8080/counter, which displays the number of hits.
Click the browser’s Refresh button to see the hit count increment.
In a terminal window, go to:
tut-install/examples/ejb/counter |
Type the following command:
ant all |
This will build and deploy counter to your GlassFish Server instance.
In a web browser, type the following URL:
http://localhost:8080/counter |
Click the browser’s Refresh button to see the hit count increment.
This example demonstrates a simple web service that generates a response based on information received from the client. HelloServiceBean is a stateless session bean that implements a single method: sayHello. This method matches the sayHello method invoked by the client described in A Simple JAX-WS Application Client.
HelloServiceBean is the endpoint implementation class, typically the primary programming artifact for enterprise bean web service endpoints. The web service endpoint implementation class has the following requirements.
The class must be annotated with either the javax.jws.WebService or the javax.jws.WebServiceProvider annotation.
The implementing class may explicitly reference an SEI through the endpointInterface element of the @WebService annotation but is not required to do so. If no endpointInterface is specified in @WebService, an SEI is implicitly defined for the implementing class.
The business methods of the implementing class must be public and must not be declared static or final.
Business methods that are exposed to web service clients must be annotated with javax.jws.WebMethod.
Business methods that are exposed to web service clients must have JAXB-compatible parameters and return types. See the list of JAXB default data type bindings at http://download.oracle.com/javaee/5/tutorial/doc/bnazq.html#bnazs.
The implementing class must not be declared final and must not be abstract.
The implementing class must have a default public constructor.
The endpoint class must be annotated @Stateless.
The implementing class must not define the finalize method.
The implementing class may use the javax.annotation.PostConstruct or javax.annotation.PreDestroy annotations on its methods for lifecycle event callbacks.
The @PostConstruct method is called by the container before the implementing class begins responding to web service clients.
The @PreDestroy method is called by the container before the endpoint is removed from operation.
The HelloServiceBean class implements the sayHello method, which is annotated @WebMethod. The source code for the HelloServiceBean class follows:
package com.sun.tutorial.javaee.ejb; import javax.ejb.Stateless; import javax.jws.WebMethod; import javax.jws.WebService; @Stateless @WebService public class HelloServiceBean { private String message = "Hello, "; public void HelloServiceBean() {} @WebMethod public String sayHello(String name) { return message + name + "."; } }
You can build, package, and deploy the helloservice example using either NetBeans IDE or Ant. You can then use the Administration Console to test the web service endpoint methods.
In NetBeans IDE, select File->Open Project.
In the Open Project dialog, navigate to:
tut-install/examples/ejb/ |
Select the helloservice folder.
Select the Open as Main Project and Open Required Projects check boxes.
Click Open Project.
In the Projects tab, right-click the helloservice project and select Deploy.
This builds and packages the application into helloservice.ear, located in tut-install/examples/ejb/helloservice/dist, and deploys this EAR file to the GlassFish Server.
In a terminal window, go to:
tut-install/examples/ejb/helloservice/ |
Type the following command:
ant |
This runs the default task, which compiles the source files and packages the application into a JAR file located at tut-install/examples/ejb/helloservice/dist/helloservice.jar.
To deploy helloservice, type the following command:
ant deploy |
Upon deployment, the GlassFish Server generates additional artifacts required for web service invocation, including the WSDL file.
The GlassFish Server Administration Console allows you to test the methods of a web service endpoint. To test the sayHello method of HelloServiceBean, follow these steps.
Open the Administration Console by opening the following URL in a web browser:
http://localhost:4848/ |
In the left pane of the Administration Console, select the Applications node.
In the Applications table, click helloservice.
In the Modules and Components table, click View Endpoint.
On the Web Service Endpoint Information page, click the Tester link:
/HelloServiceBeanService/HelloServiceBean?Tester |
The tester page opens in a browser window or tab.
Under Methods, type a name as the parameter to the sayHello method.
Click the sayHello button.
The sayHello Method invocation page opens. Under Method returned, you’ll see the response from the endpoint.
Applications that model business work flows often rely on timed notifications. The timer service of the enterprise bean container enables you to schedule timed notifications for all types of enterprise beans except for stateful session beans. You can schedule a timed notification to occur according to a calendar schedule, at a specific time, after a duration of time, or at timed intervals. For example, you could set timers to go off at 10:30 a.m. on May 23, in 30 days, or every 12 hours.
Enterprise bean timers are either programmatic timers or automatic timers. Programmatic timers are set by explicitly calling one of the timer creation methods of the TimerService interface. Automatic timers are created upon the successful deployment of an enterprise bean that contains a method annotated with the java.ejb.Schedule or java.ejb.Schedules annotations.
Timers can be set according to a calendar-based schedule, expressed using a syntax similar to the UNIX cron utility. Both programmatic and automatic timers can use calendar-based timer expressions. Table 16–1 shows the calendar-based timer attributes.
Table 16–1 Calendar-Based Timer Attributes
Attribute |
Description |
Allowable Values |
Default Value |
Examples |
---|---|---|---|---|
second |
One or more seconds within a minute |
0 to 59 |
0 |
second="30" |
minute |
One or more minutes within an hour |
0 to 59 |
0 |
minute="15" |
hour |
One or more hours within a day |
0 to 23 |
0 |
hour="13" |
dayOfWeek |
One or more days within a week |
0 to 7 (both 0 and 7 refer to Sunday) Sun, Mon, Tue, Wed, Thu, Fri, Sat |
* |
dayOfWeek="3" dayOfWeek="Mon" |
dayOfMonth |
One or more days within a month |
1 to 31 –7 to –1 (a negative number means the nth day or days before the end of the month) Last [1st, 2nd, 3rd, 4th, 5th, Last] [Sun, Mon, Tue, Wed, Thu, Fri, Sat] |
* |
dayOfMonth="15" dayOfMonth="–3" dayOfMonth="Last" dayOfMonth="2nd Fri" |
month |
One or more months within a year |
1 to 12 Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec |
* |
month="7" month="July" |
year |
A particular calendar year |
A four–digit calendar year |
* |
year="2010" |
You can specify multiple values in calendar expressions, as described in the following sections.
Setting an attribute to an asterisk symbol (*) represents all allowable values for the attribute.
The following expression represents every minute:
minute="*"
The following expression represents every day of the week:
dayOfWeek="*"
To specify two or more values for an attribute, use a comma (,) to separate the values. A range of values is allowed as part of a list. Wildcards and intervals, however, are not allowed.
Duplicates within a list are ignored.
The following expression sets the day of the week to Tuesday and Thursday:
dayOfWeek="Tue, Thu"
The following expression represents 4:00 a.m., every hour from 9:00 a.m. to 5:00 p.m. using a range, and 10:00 p.m.:
hour="4,9–17,22"
Use a dash character (–) to specify an inclusive range of values for an attribute. Members of a range cannot be wildcards, lists, or intervals. A range of the form x–x, is equivalent to the single-valued expression x. A range of the form x–y where x is greater than y is equivalent to the expression x–maximum value, minimum value–y. That is, the expression begins at x, rolls over to the beginning of the allowable values, and continues up to y.
The following expression represents 9:00 a.m. to 5:00 p.m.:
hour="9–17"
The following expression represents Friday through Monday:
dayOfWeek="5–1"
The following expression represents the twenty-fifth day of the month to the end of the month, and the beginning of the month to the fifth day of the month:
dayOfMonth="25–5"
It is equivalent to the following expression:
dayOfMonth="25–Last,1–5"
The forward slash (/) constrains an attribute to a starting point and an interval and is used to specify every N seconds, minutes, or hours within the minute, hour, or day. For an expression of the form x/y, x represents the starting point and y represents the interval. The wildcard character may be used in the x position of an interval and is equivalent to setting x to 0.
Intervals may be set only for second, minute, and hour attributes.
The following expression represents every 10 minutes within the hour:
minute="*/10"
It is equivalent to:
minute="0,10,20,30,40,50"
The following expression represents every 2 hours starting at noon:
hour="12/2"
When a programmatic timer expires (goes off), the container calls the method annotated @Timeout in the bean’s implementation class. The @Timeout method contains the business logic that handles the timed event.
Methods annotated @Timeout in the enterprise bean class must return void and optionally take a javax.ejb.Timer object as the only parameter. They may not throw application exceptions.
@Timeout public void timeout(Timer timer) { System.out.println("TimerBean: timeout occurred"); }
To create a timer, the bean invokes one of the create methods of the TimerService interface. These methods allow single-action, interval, or calendar-based timers to be created.
For single-action or interval timers, the expiration of the timer can be expressed as either a duration or an absolute time. The duration is expressed as a the number of milliseconds before a timeout event is triggered. To specify an absolute time, create a java.util.Date object and pass it to the TimerService.createSingleActionTimer or the TimerService.createTimer method.
The following code sets a programmatic timer that will expire in 1 minute (6,000 milliseconds):
long duration = 6000; Timer timer = timerService.createSingleActionTimer(duration, new TimerConfig());
The following code sets a programmatic timer that will expire at 12:05 p.m. on May 1, 2010, specified as a java.util.Date:
SimpleDateFormatter formatter = new SimpleDateFormatter("MM/dd/yyyy 'at' HH:mm"); Date date = formatter.parse("05/01/2010 at 12:05"); Timer timer = timerService.createSingleActionTimer(date, new TimerConfig());
For calendar-based timers, the expiration of the timer is expressed as a javax.ejb.ScheduleExpression object, passed as a parameter to the TimerService.createCalendarTimer method. The ScheduleExpression class represents calendar-based timer expressions and has methods that correspond to the attributes described in Creating Calendar-Based Timer Expressions.
The following code creates a programmatic timer using the ScheduleExpression helper class:
ScheduleExpression schedule = new ScheduleExpression(); schedule.dayOfWeek("Mon"); schedule.hour("12-17, 23"); Timer timer = timerService.createCalendarTimer(schedule);
For details on the method signatures, see the TimerService API documentation at http://download.oracle.com/javaee/6/api/javax/ejb/TimerService.html.
The bean described in The timersession Example creates a timer as follows:
Timer timer = timerService.createTimer(intervalDuration, "Created new programmatic timer");
In the timersession example, createTimer is invoked in a business method, which is called by a client.
Timers are persistent by default. If the server is shut down or crashes, persistent timers are saved and will become active again when the server is restarted. If a persistent timer expires while the server is down, the container will call the @Timeout method when the server is restarted.
Nonpersistent programmatic timers are created by calling TimerConfig.setPersistent(false) and passing the TimerConfig object to one of the timer-creation methods.
The Date and long parameters of the createTimer methods represent time with the resolution of milliseconds. However, because the timer service is not intended for real-time applications, a callback to the @Timeout method might not occur with millisecond precision. The timer service is for business applications, which typically measure time in hours, days, or longer durations.
Automatic timers are created by the EJB container when an enterprise bean that contains methods annotated with the @Schedule or @Schedules annotations is deployed. An enterprise bean can have multiple automatic timeout methods, unlike a programmatic timer, which allows only one method annotated with the @Timeout annotation in the enterprise bean class.
Automatic timers can be configured through annotations or through the ejb-jar.xml deployment descriptor.
Adding a @Schedule annotation on an enterprise bean marks that method as a timeout method according to the calendar schedule specified in the attributes of @Schedule.
The @Schedule annotation has elements that correspond to the calendar expressions detailed in Creating Calendar-Based Timer Expressions and the persistent, info, and timezone elements.
The optional persistent element takes a Boolean value and is used to specify whether the automatic timer should survive a server restart or crash. By default, all automatic timers are persistent.
The optional timezone element is used to specify that the automatic timer is associated with a particular time zone. If set, this element will evaluate all timer expressions in relation to the specified time zone, regardless of the time zone in which the EJB container is running. By default, all automatic timers set are in relation to the default time zone of the server.
The optional info element is used to set an informational description of the timer. A timer’s information can be retrieved later by using Timer.getInfo.
The following timeout method uses @Schedule to set a timer that will expire every Sunday at midnight:
@Schedule(dayOfWeek="Sun", hour="0") public void cleanupWeekData() { ... }
The @Schedules annotation is used to specify multiple calendar-based timer expressions for a given timeout method.
The following timeout method uses the @Schedules annotation to set multiple calendar-based timer expressions. The first expression sets a timer to expire on the last day of every month. The second expression sets a timer to expire every Friday at 11:00 p.m.
@Schedules ({ @Schedule(dayOfMonth="Last"), @Schedule(dayOfWeek="Fri", hour="23") }) public void doPeriodicCleanup() { ... }
Timers can be canceled by the following events.
When a single-event timer expires, the EJB container calls the associated timeout method and then cancels the timer.
When the bean invokes the cancel method of the Timer interface, the container cancels the timer.
If a method is invoked on a canceled timer, the container throws the javax.ejb.NoSuchObjectLocalException.
To save a Timer object for future reference, invoke its getHandle method and store the TimerHandle object in a database. (A TimerHandle object is serializable.) To reinstantiate the Timer object, retrieve the handle from the database and invoke getTimer on the handle. A TimerHandle object cannot be passed as an argument of a method defined in a remote or web service interface. In other words, remote clients and web service clients cannot access a bean’s TimerHandle object. Local clients, however, do not have this restriction.
In addition to defining the cancel and getHandle methods, the Timer interface defines methods for obtaining information about timers:
public long getTimeRemaining(); public java.util.Date getNextTimeout(); public java.io.Serializable getInfo();
The getInfo method returns the object that was the last parameter of the createTimer invocation. For example, in the createTimer code snippet of the preceding section, this information parameter is a String object with the value created timer.
To retrieve all of a bean’s active timers, call the getTimers method of the TimerService interface. The getTimers method returns a collection of Timer objects.
An enterprise bean usually creates a timer within a transaction. If this transaction is rolled back, the timer creation also is rolled back. Similarly, if a bean cancels a timer within a transaction that gets rolled back, the timer cancellation is rolled back. In this case, the timer’s duration is reset as if the cancellation had never occurred.
In beans that use container-managed transactions, the @Timeout method usually has the Required or RequiresNew transaction attribute to preserve transaction integrity. With these attributes, the EJB container begins the new transaction before calling the @Timeout method. If the transaction is rolled back, the container will call the @Timeout method at least one more time.
The source code for this example is in the tut-install/examples/ejb/timersession/src/java/ directory.
TimerSessionBean is a singleton session bean that shows how to set both an automatic timer and a programmatic timer. In the source code listing of TimerSessionBean that follows, the setTimer and @Timeout methods are used to set a programmatic timer. A TimerService instance is injected by the container when the bean is created. Because it’s a business method, setTimer is exposed to the local, no-interface view of TimerSessionBean and can be invoked by the client. In this example, the client invokes setTimer with an interval duration of 30,000 milliseconds. The setTimer method creates a new timer by invoking the createTimer method of TimerService. Now that the timer is set, the EJB container will invoke the programmaticTimeout method of TimerSessionBean when the timer expires, in about 30 seconds.
... public void setTimer(long intervalDuration) { logger.info("Setting a programmatic timeout for " + intervalDuration + " milliseconds from now."); Timer timer = timerService.createTimer(intervalDuration, "Created new programmatic timer"); } @Timeout public void programmaticTimeout(Timer timer) { this.setLastProgrammaticTimeout(new Date()); logger.info("Programmatic timeout occurred."); } ...
TimerSessionBean also has an automatic timer and timeout method, automaticTimeout. The automatic timer is set to expire every 3 minutes and is set by using a calendar-based timer expression in the @Schedule annotation:
... @Schedule(minute="*/3", hour="*") public void automaticTimeout() { this.setLastAutomaticTimeout(new Date()); logger.info("Automatic timeout occured"); } ...
TimerSessionBean also has two business methods: getLastProgrammaticTimeout and getLastAutomaticTimeout. Clients call these methods to get the date and time of the last timeout for the programmatic timer and automatic timer, respectively.
Here’s the source code for the TimerSessionBean class:
package timersession.ejb; import java.util.Date; import java.util.logging.Logger; import javax.annotation.Resource; import javax.ejb.Schedule; import javax.ejb.Stateless; import javax.ejb.Timeout; import javax.ejb.Timer; import javax.ejb.TimerService; @Singleton public class TimerSessionBean { @Resource TimerService timerService; private Date lastProgrammaticTimeout; private Date lastAutomaticTimeout; private Logger logger = Logger.getLogger( "com.sun.tutorial.javaee.ejb.timersession.TimerSessionBean"); public void setTimer(long intervalDuration) { logger.info("Setting a programmatic timeout for " + intervalDuration + " milliseconds from now."); Timer timer = timerService.createTimer(intervalDuration, "Created new programmatic timer"); } @Timeout public void programmaticTimeout(Timer timer) { this.setLastProgrammaticTimeout(new Date()); logger.info("Programmatic timeout occurred."); } @Schedule(minute="*/3", hour="*") public void automaticTimeout() { this.setLastAutomaticTimeout(new Date()); logger.info("Automatic timeout occured"); } public String getLastProgrammaticTimeout() { if (lastProgrammaticTimeout != null) { return lastProgrammaticTimeout.toString(); } else { return "never"; } } public void setLastProgrammaticTimeout(Date lastTimeout) { this.lastProgrammaticTimeout = lastTimeout; } public String getLastAutomaticTimeout() { if (lastAutomaticTimeout != null) { return lastAutomaticTimeout.toString(); } else { return "never"; } } public void setLastAutomaticTimeout(Date lastAutomaticTimeout) { this.lastAutomaticTimeout = lastAutomaticTimeout; } }
GlassFish Server has a default minimum timeout value of 1,000 milliseconds, or 1 second. If you need to set the timeout value lower than 1,000 milliseconds, change the value of the minimum-delivery-interval-in-millis element in domain-dir/config/domain.xml. The lowest practical value for minimum-delivery-interval-in-millis is around 10 milliseconds, owing to virtual machine constraints.
You can build, package, deploy, and run the timersession example by using either NetBeans IDE or Ant.
In NetBeans IDE, select File->Open Project.
In the Open Project dialog, navigate to:
tut-install/examples/ejb/ |
Select the timersession folder.
Select the Open as Main Project check box.
Click Open Project.
Select Run->Run Main Project.
This builds and packages the application into timersession.war, located in tut-install/examples/ejb/timersession/dist/, deploys this WAR file to your GlassFish Server instance, and then runs the web client.
In a terminal window, go to:
tut-install/examples/ejb/timersession/ |
Type the following command:
ant build |
This runs the default task, which compiles the source files and packages the application into a WAR file located at tut-install/examples/ejb/timersession/dist/timersession.war.
To deploy the application, type the following command:
ant deploy |
Open a web browser to http://localhost:8080/timersession.
Click the Set Timer button to set a programmatic timer.
Wait for a while and click the browser’s Refresh button.
You will see the date and time of the last programmatic and automatic timeouts.
To see the messages that are logged when a timeout occurs, open the server.log file located in domain-dir/server/logs/.
The exceptions thrown by enterprise beans fall into two categories: system and application.
A system exception indicates a problem with the services that support an application. For example, a connection to an external resource cannot be obtained, or an injected resource cannot be found. If it encounters a system-level problem, your enterprise bean should throw a javax.ejb.EJBException. Because the EJBException is a subclass of the RuntimeException, you do not have to specify it in the throws clause of the method declaration. If a system exception is thrown, the EJB container might destroy the bean instance. Therefore, a system exception cannot be handled by the bean’s client program, but instead requires intervention by a system administrator.
An application exception signals an error in the business logic of an enterprise bean. Application exceptions are typically exceptions that you’ve coded yourself, such as the BookException thrown by the business methods of the CartBean example. When an enterprise bean throws an application exception, the container does not wrap it in another exception. The client should be able to handle any application exception it receives.
If a system exception occurs within a transaction, the EJB container rolls back the transaction. However, if an application exception is thrown within a transaction, the container does not roll back the transaction.
Message-driven beans can implement any messaging type. Most commonly, they implement the Java Message Service (JMS) technology. The example in this chapter uses JMS technology, so you should be familiar with basic JMS concepts such as queues and messages. To learn about these concepts, see Chapter 30, Java Message Service Concepts.
This chapter describes the source code of a simple message-driven bean example. Before proceeding, you should read the basic conceptual information in the section What Is a Message-Driven Bean? as well as Using Message-Driven Beans to Receive Messages Asynchronously.
The following topics are addressed here:
The simplemessage application has the following components:
SimpleMessageClient: An application client that sends several messages to a queue
SimpleMessageBean: A message-driven bean that asynchronously receives and processes the messages that are sent to the queue
Figure 17–1 illustrates the structure of this application. The application client sends messages to the queue, which was created administratively using the Administration Console. The JMS provider (in this case, the GlassFish Server) delivers the messages to the instances of the message-driven bean, which then processes the messages.
The source code for this application is in the tut-install/examples/ejb/simplemessage/ directory.
The SimpleMessageClient sends messages to the queue that the SimpleMessageBean listens to. The client starts by injecting the connection factory and queue resources:
@Resource(mappedName="jms/ConnectionFactory") private static ConnectionFactory connectionFactory; @Resource(mappedName="jms/Queue") private static Queue queue;
Next, the client creates the connection, session, and message producer:
connection = connectionFactory.createConnection(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); messageProducer = session.createProducer(queue);
Finally, the client sends several messages to the queue:
message = session.createTextMessage(); for (int i = 0; i < NUM_MSGS; i++) { message.setText("This is message " + (i + 1)); System.out.println("Sending message: " + message.getText()); messageProducer.send(message); }
The code for the SimpleMessageBean class illustrates the requirements of a message-driven bean class:
It must be annotated with the @MessageDriven annotation if it does not use a deployment descriptor.
The class must be defined as public.
The class cannot be defined as abstract or final.
It must contain a public constructor with no arguments.
It must not define the finalize method.
It is recommended, but not required, that a message-driven bean class implement the message listener interface for the message type it supports. A bean that supports the JMS API implements the javax.jms.MessageListener interface.
Unlike session beans and entities, message-driven beans do not have the remote or local interfaces that define client access. Client components do not locate message-driven beans and invoke methods on them. Although message-driven beans do not have business methods, they may contain helper methods that are invoked internally by the onMessage method.
For the GlassFish Server, the @MessageDriven annotation typically contains a mappedName element that specifies the JNDI name of the destination from which the bean will consume messages. For complex message-driven beans, there can also be an activationconfig element containing @ActivationConfigProperty annotations used by the bean.
A message-driven bean can also inject a MessageDrivenContext resource. Commonly you use this resource to call the setRollbackOnly method to handle exceptions for a bean that uses container-managed transactions.
Therefore, the first few lines of the SimpleMessageBean class look like this:
@MessageDriven(mappedName="jms/Queue", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") }) public class SimpleMessageBean implements MessageListener { @Resource private MessageDrivenContext mdc; ...
NetBeans IDE typically creates a message-driven bean with a default set of @ActivationConfigProperty settings. You can delete those you do not need, or add others. Table 17–1 lists commonly used properties.
Table 17–1 @ActivationConfigProperty Settings for Message-Driven Beans
Property Name |
Description |
---|---|
acknowledgeMode |
Acknowledgment mode; see Controlling Message Acknowledgment for information |
destinationType |
Either javax.jms.Queue or javax.jms.Topic |
subscriptionDurability |
For durable subscribers, set to Durable; see Creating Durable Subscriptions for information |
clientId |
For durable subscribers, the client ID for the connection |
subscriptionName |
For durable subscribers, the name of the subscription |
messageSelector |
A string that filters messages; see JMS Message Selectors for information, and see An Application That Uses the JMS API with a Session Bean for an example |
addressList |
Remote system or systems to communicate with; see An Application Example That Consumes Messages from a Remote Server for an example |
When the queue receives a message, the EJB container invokes the message listener method or methods. For a bean that uses JMS, this is the onMessage method of the MessageListener interface.
A message listener method must follow these rules:
The method must be declared as public.
The method must not be declared as final or static.
The onMessage method is called by the bean’s container when a message has arrived for the bean to service. This method contains the business logic that handles the processing of the message. It is the message-driven bean’s responsibility to parse the message and perform the necessary business logic.
The onMessage method has a single argument: the incoming message.
The signature of the onMessage method must follow these rules:
The return type must be void.
The method must have a single argument of type javax.jms.Message.
In the SimpleMessageBean class, the onMessage method casts the incoming message to a TextMessage and displays the text:
public void onMessage(Message inMessage) { TextMessage msg = null; try { if (inMessage instanceof TextMessage) { msg = (TextMessage) inMessage; logger.info("MESSAGE BEAN: Message received: " + msg.getText()); } else { logger.warning("Message of wrong type: " + inMessage.getClass().getName()); } } catch (JMSException e) { e.printStackTrace(); mdc.setRollbackOnly(); } catch (Throwable te) { te.printStackTrace(); } }
To package, deploy and run this example, go to the tut-install/examples/ejb/simplemessage/ directory.
This example requires the following:
A JMS connection factory resource
A JMS destination resource
If you have run the simple JMS examples in Chapter 30, Java Message Service Concepts and have not deleted the resources, you already have these resources and do not need to perform these steps.
You can use Ant targets to create the resources. The Ant targets, which are defined in the build.xml file for this example, use the asadmin command. To create the resources needed for this example, use the following commands:
ant create-cf ant create-queue |
These commands do the following:
Create a connection factory resource named jms/ConnectionFactory
Create a destination resource named jms/Queue
The Ant targets for these commands refer to other targets that are defined in the tut-install/examples/bp-project/app-server-ant.xml file.
In NetBeans IDE, select File->Open Project.
In the Open Project dialog, navigate to:
tut-install/examples/ejb/ |
Select the simplemessage folder.
Select the Open as Main Project check box and the Open Required Projects check box.
Click Open Project.
In the Projects tab, right-click the simplemessage project and choose Build.
This task packages the application client and the message-driven bean, then creates a file named simplemessage.ear in the dist directory.
Right-click the project and choose Run.
This command deploys the project, returns a JAR file named simplemessageClient.jar, and then executes it.
The output of the application client in the Output pane looks like this (preceded by application client container output):
Sending message: This is message 1 Sending message: This is message 2 Sending message: This is message 3 To see if the bean received the messages, check <install_dir>/domains/domain1/logs/server.log. |
The output from the message-driven bean appears in the server log (domain-dir/logs/server.log), wrapped in logging information.
MESSAGE BEAN: Message received: This is message 1 MESSAGE BEAN: Message received: This is message 2 MESSAGE BEAN: Message received: This is message 3 |
The received messages may appear in a different order from the order in which they were sent.
Go to the following directory:
tut-install/examples/ejb/simplemessage/
To compile the source files and package the application, use the following command:
ant |
This target packages the application client and the message-driven bean, then creates a file named simplemessage.ear in the dist directory.
By using resource injection and annotations, you avoid having to create deployment descriptor files for the message-driven bean and application client. You need to use deployment descriptors only if you want to override the values specified in the annotated source files.
To deploy the application and run the client using Ant, use the following command:
ant run |
Ignore the message that states that the application is deployed at a URL.
The output in the terminal window looks like this (preceded by application client container output):
Sending message: This is message 1 Sending message: This is message 2 Sending message: This is message 3 To see if the bean received the messages, check <install_dir>/domains/domain1/logs/server.log. |
In the server log file, the following lines appear, wrapped in logging information:
MESSAGE BEAN: Message received: This is message 1 MESSAGE BEAN: Message received: This is message 2 MESSAGE BEAN: Message received: This is message 3 |
The received messages may appear in a different order from the order in which they were sent.
After you run the example, you can use the following Ant targets to delete the connection factory and queue:
ant delete-cf ant delete-queue |