public float getBalanceint accountId throws RemoteException { Context context = new InitialContext ; // Look up the Account bean AccountHome accountHome = AccountHome public Collec
Trang 1public float getBalance(int accountId) throws RemoteException {
Context context = new InitialContext( );
// Look up the Account bean
AccountHome accountHome = (AccountHome)
public Collection findByUserId(Integer userId)
throws FinderException, RemoteException;
The accompanying query element in the Account bean's entry in the ejb-jar.xml descriptor
would look like this:
Trang 28.3.2 Going Stateless
To move this bean into stateless territory, you first need to change the home interface's create( ) signature Since stateless beans can't maintain any information between method calls, passing in a username (or any other data) to the create( ) method is useless Make the following change:
public AccountManager create( )
throws CreateException, RemoteException;
Once this change has been made, you need to determine which methods advertised by the bean require a username for operation In other words, browse through your bean's implementation class and note any method that uses the username or user method variable Once you've determined the methods in this category, you will need to change the signature for those methods in the remote interface:
public interface AccountManager extends EJBObject {
public AccountInfo add(String username, String type, float balance)
throws RemoteException, UnknownAccountTypeException;
public float getBalance(int accountId) throws RemoteException;
public boolean delete(int accountId) throws RemoteException;
Trang 3your bean implementation class to operate without maintaining state First, add a utility method to the end of the class:
private User getUser(String username) throws RemoteException {
try {
// Get an InitialContext
Context context = new InitialContext( );
// Look up the Account bean
UserHome userHome = (UserHome)
public void ejbCreate( ) throws CreateException {
// Nothing to be done for stateless beans
// Get the correct user
User user = getUser(username);
// Look up the Account bean
AccountHome accountHome = (AccountHome)
public List getAll(String username) {
List accounts = new LinkedList( );
try {
User user = getUser(username);
Integer userId = user.getId( );
// Get an InitialContext
Trang 4// Look up the Account bean
AccountHome accountHome = (AccountHome)
context.lookup("java:comp/env/ejb/AccountHome");
Collection userAccounts = accountHome.findByUserId(userId);
for (Iterator i = userAccounts.iterator(); i.hasNext( ); ) {
Account account = (Account)i.next( );
Trang 5Example 8-10 An AccountManager Helper Class
/** The username for this account's user */
private String username;
/** The <code>AccountManager</code> bean instance */
private AccountManager manager;
public AccountManagerHelper(String username)
throws CreateException, NamingException, RemoteException {
// Get the stateless bean instance
Object ref = context.lookup("forethought.AccountManagerHome"); AccountManagerHome accountManagerHome = (AccountManagerHome)
PortableRemoteObject.narrow(ref, AccountManagerHome.class); this.manager = accountManagerHome.create( );
}
public AccountInfo add(String type, float balance)
throws RemoteException, UnknownAccountTypeException {
Trang 6public AccountInfo withdraw(AccountInfo accountInfo, float amount) throws RemoteException {
// Look up the AccountManager bean
System.out.println("Looking up the AccountManager bean.");
// Get all accounts
List accounts = accountHelper.getAll( );
for (Iterator i = accounts.iterator(); i.hasNext( ); ) {
AccountInfo accountInfo = (AccountInfo)i.next( );
System.out.println("Account ID: " + accountInfo.getId( ));
System.out.println("Account Type: " + accountInfo.getType( ));
Trang 7// Delete account
accountHelper.delete(everydayAccount.getId( ));
System.out.println("Deleted everyday account.");
You may find that helper classes like this can simplify your own client code, even if you don't
need to provide stateful session bean masquerading, where a stateless bean is made to look
like a stateful one In any case, this approach provides the best of both session bean types: the performance of a stateless bean with the interface of a stateful one This technique will allow you to convert all of your application's stateful session beans into stateless ones, which will yield some dramatic performance improvements
8.4 What's Next?
You now have the tools to build the back-end of almost any enterprise application you may come across, and apply your knowledge to most of the problems you will encounter in the enterprise Java space In the next chapter, though, I want to move beyond the basics into the less-used realm of the Java Message Service (and specifically, message-driven beans) Although it is still somewhat unusual to see these kinds of beans in action, you will find that JMS offers several attractive features I'll detail these and how they can help in asynchronous tasks in the next chapter, which focuses specifically on messaging in enterprise applications
Trang 8Chapter 9 Messaging and Packaging
Up until now, everything detailed in the Forethought application has been based on
synchronous processing This simply means that an event is triggered by some client, then
responded to by an application component, and finally an answer is returned to that client For example, when a Java class requests that a new user be created, the UserManager accesses the User bean, that bean interacts with the database, and an acknowledgment is triggered back up the calling stack The extensive coverage of this type of interaction is justified, as you will be dealing with synchronous processing far more often than not
However, there are times when you want more of a listener paradigm In this case, an application component waits for certain types of events and responds only when those events
occur That component is called a listener, because it listens for application events When it is
activated, it takes some sort of action, often interacting with various other components in the application It typically does not send any acknowledgment when its actions are done, making
it asynchronous in operation I'll detail this sort of behavior in this chapter, focusing on the scheduling component of the Forethought application Meetings will be added to the Forethought queue and reported to a scheduling client, which simply spits these meetings back out to waiting recipients
Additionally, this chapter will wrap up some loose ends by detailing the final packaging of the enterprise beans detailed in this and previous chapters This will fill in the blanks on assembly descriptors, method permissions, and other deployment descriptor options previously left uncovered At the end of this chapter, you'll have a complete, working application foundation, ready for use
9.1 Messaging on the Server
To begin the discussion on messaging, I want to focus on the scheduling component of the Forethought application Specifically, I want to look at messaging components on the server
By "the server," I simply mean the back-end of the application, as distinguished from any set
of application clients This may or may not be a separate physical machine, but in either case,
it is distinct from application clients such as desktop programs or other application interface tools Once you understand how this messaging operates within the application, you will be ready for the next section, where clients are discussed and built
9.1.1 Premise
First, let's revisit the scheduler facility for the Forethought application The application should
be able to store events that are important to the company As you recall from Chapter 3, the EVENTS table is set up for just such a purpose Then, users in the Forethought application are associated with these events and become attendees (not surprisingly, stored in the ATTENDEES table) This is all fairly basic material
Scheduling comes into play when you realize that individual employees will probably run some type of calendar or scheduling software on their computers For the sake of this discussion, assume that this software is customizable, and that you can add features to it That
is important, as it allows the messaging and scheduling components in the application to be hooked into their desktop software Given that assumption, the task becomes clear
Trang 9Each time a new event is added to the data store, a message should be fired off This message should indicate that a new event has been created, and also include the attendees for that event Since you should already have an Event entity bean (from Appendix E), it is fairly easy
to extrapolate the need for a session bean to handle the addition of data to that bean, as discussed in the last chapter I'll call this session bean Scheduler While it could have been called EventManager, I've used a different name to indicate that it is not a simple administrative component, as the other manager beans were This component will handle creation of events, and then send off a Java Message Service (JMS) message indicating this creation
The purpose of this message is simple: it allows any application client subscribed to the same JMS topic to which these messages are sent to consume the new message The client can examine the new event, and if the event has a certain individual as an attendee, it can sound
an alarm, send email, or otherwise notify the relevant attendee I'll delve into specific examples of these actions in Section 9.2 However, understand that once your component makes these messages available through JMS, the possibilities for client interaction become nearly limitless
9.1.2 The EventManager Bean
Actually putting these principles into practice is not nearly as complex as you might expect First, you should already have the Event bean coded from Appendix E You'll then need to code up a manager session bean to allow access to this entity I've kept this bean extremely simple, as it's not the focus of this discussion Example 9-1 shows the remote interface for this new manager
Example 9-1 The EventManager Remote Interface
public interface EventManager extends EJBObject {
public EventInfo addEvent(String description, Date dateTime,
Trang 10Example 9-2 The EventManager Home Interface
package com.forethought.ejb.event;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface EventManagerHome extends EJBHome {
public EventManager create( )
throws CreateException, RemoteException;
/** <p> Required method for allowing bean lookups </p> */
public void ejbCreate( ) throws CreateException {
// No action required for stateless session beans
Context context = new InitialContext( );
// Add event to database
EventHome eventHome = (EventHome)
Trang 11// Get topic factory
// Send off notification of this event
Trang 12Context context = new InitialContext( );
// Look up the User bean
EventHome eventHome = (EventHome)
Trang 13the message body Additionally, some indication of what action has occurred needs to be included Rather than putting this application-specific data into the information map, a string property of the message is set, which will be decoded by message recipients The result is a simple yet useful message that is broadcast upon creation and deletion of events in the Forethought system This completes the EventManager bean, and allows us to move on to a message-driven bean that will consume and use these messages
9.1.3 Business Logic and Messaging Logic
As in the case of using pure JMS, described in the last section, I am not going to discuss the basics of message-driven beans Instead, I'll simply show you a new bean for your application, the Scheduler bean, which consumes messages from the EventManager bean just created One nice thing about message-driven beans is that they are completely server-based components This means that no remote or home interface is required Given that, Example 9-4 jumps straight to the implementation code for this new bean
Example 9-4 The Scheduler Bean
public class SchedulerBean implements MessageDrivenBean {
/** The context for the message-driven bean, set by the EJB container
*/
private MessageDrivenContext messageContext;
/** Required creation method for message-driven beans */
public void ejbCreate( ) {
// No action required for message-driven beans
}
Trang 14
/** Required removal method for message-driven beans */
public void ejbRemove( ) {
messageContext = null;
}
/** Required method for container to set context */
public void setMessageDrivenContext(MessageDrivenContext
// Convert to the correct message type
ObjectMessage objectMessage = (ObjectMessage)message;
// Dissect message
String action = objectMessage.getStringProperty("Action");
EventInfo eventInfo = (EventInfo)objectMessage.getObject( );
private void sendEventNotification(String action, EventInfo eventInfo)
throws JMSException, NamingException {
// Ensure that at least one employee involved
boolean hasEmployee = false;
for (Iterator i = eventInfo.getAttendees().iterator();
// Get the client topic destination
Context context = new InitialContext( );
Trang 15// Send off notification of this event
TopicSession session =
connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); TopicPublisher publisher = session.createPublisher(topic);
other publisher to the Scheduler topic It then sends out new messages to the Employee topic
(introduced for the first time here), using another ObjectMessage, through the sendEventNotification( ) method Given that this bean does little more than resend messages, you are probably wondering why it even exists This is a good question, and has a good answer
The EventManager bean could have easily sent messages directly to the Employee topic,
handling filtering on its own However, that assumes that no other action needs to be taken For example, a more advanced Scheduler bean might update a company-wide directory server with the event information, log the changes to a static text file, and update a scheduling database using entity beans This logic is specific to scheduling, not simple creation and addition of events If all of this scheduling logic were added into the EventManager component, it would quickly become unclear what code in that bean was specifically event-related, and what code was scheduling-related In other words, the bean would quickly cease
to be a simple manager/administrative component
Further, that scenario presumes that only the EventManager takes action related to scheduling It's plausible that other beans, Java classes, or messaging clients might also be able to update events, change attendees, or perform other scheduling-related actions By taking all scheduling-related code and placing it in a separate bean, any additional logic can be maintained in a single place (the Scheduler bean) Finally, this design allows changes to scheduling logic to occur without having to make the EventManager unavailable; if this happened, the EventManager would simply be sending messages out that would be consumed
at a later date (or not at all, as the case may be) In any case, it should be clear that separating your messaging logic from your data logic is critical, and explains the reasoning behind a separate Scheduler bean Don't be fooled by the simplicity of the current Scheduler implementation; things in a real-world system would quickly become more complex than shown here
You should now follow the appendixes' and your server's instructions to deploy these new components, including the message-driven bean Once those resources are in place, it's time to look at writing a standalone Java client that takes these messages and does something with them