Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 113 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
113
Dung lượng
1,59 MB
Nội dung
Java Message Service 427 9 Q What type of JMS message domain should be used to send a message to many receivers at the same time? A To send to many receivers, the publish/subscribe message domain is the best choice. Q What is the difference between JMSHeader fields and JMSProperty fields? A JMS header fields are defined in the JMS API and are mainly set by the JMS provider. JMS property fields are used by clients to add additional header informa- tion to a message. Q Does JMS guarantee to deliver a message in the point-to-point domain? A Messages in the point-to-point domain are PERSISTENT by default and will be delivered unless they have a timeout that has expired. Point-to-point messages can be set to NON_PERSISTENT, in which case, the message may be lost if a provider fails. Q When should I use a durable subscription? A Durable subscriptions should be used when a subscriber needs to receive messages from a topic when it is inactive. Exercise To extend your knowledge of the subjects covered today, try the following exercises. 1. Create a chat room application. Participants provide their name and can send mes- sages to any topic (hint: use a JMS property to define the topic). Participants may read messages posted by all other participants or filter by topic. You may use pre- defined topic names. To assist you in this task, three Java files have been provided in the exercise sub-direc- tory for Day 9 on the accompanying CD-ROM. The Chat.java and ChatDisplay.java files are complete and need not be edited. These files provide the Swing code to enter and display the chat room messages onscreen. The TopicServer.java is a starting point for you to further develop the chat server. The initial code simply uses the callback method addMessage to bounce the message back to the screen. The addMessage method uses the interface defined in ChatDisplay.java. You will need to edit this file to replace this callback with code to publish the message to a topic. You then need to add a subscriber that consumes messages from this topic and displays them onscreen. 12 0672323842 CH09 3/20/02 9:23 AM Page 427 Add a property called From to the message and set it to the from parameter passed in. This will then be displayed in the chat room window. A completed TopicServer is included in the solutions sub-directory of Day 9 of the case study. 428 Day 9 12 0672323842 CH09 3/20/02 9:23 AM Page 428 DAY 10 WEEK 2 Message-Driven Beans So far, you have looked at two types of Enterprise Java Bean (EJB)—the Session bean and the Entity bean. Today you will consider the third and final EJB, the Message-driven bean. Topics that are covered are as follows: • Similarities and differences with Entity and Session beans • The life-cycle of a Message-driven bean • Writing a Message-driven bean Prior to the EJB 2.0 specification, it was only possible to support asynchronous message passing by writing an external Java program that acted as a listener. A listener is a program whose sole purpose is to wait for data to arrive, for exam- ple, a socket server program that “listens” on a socket and perform some action when it detects client connections. The listener was then able to invoke methods on a session or entity bean. All EJB method calls had to be synchronous and initiated by the client. This approach had the disadvantage that the message was received outside of the server, so it could not be part of an EJB transaction. With the release of J2EE 1.3, you can use Message-driven beans to combine the functionality of EJBs with the Java Message Service (JMS). 13 0672323842 CH10 3/20/02 9:28 AM Page 429 Although JMS was covered in detail on Day 9, “Java Message Service,” the following is a quick recap of its main features: • JMS is a Java API that specifies how applications can create, send, receive, and read messages. • JMS enables communication that is both asynchronous and reliable, while mini- mizing the amount of knowledge and programming that is required. • The implementation of the JMS API is provided by a number of vendors who are known as JMS providers. • Message queues are associated with the point-to-point message domain. Messages in a queue are persistent but can only be consumed by one receiver. • Topics allow a message to be sent to more then one receiver (called a subscriber). Messages are not persistent; they are immediately delivered to all existing sub- scribers. What Are Message-Driven Beans? Message-driven beans are generally constructed to be message consumers, although they can, like any other EJB, also be used to create and send messages. A Message-driven bean lives entirely within the container, it has no security context of its own. When the bean is deployed, it is associated with a particular queue or topic, and is invoked by the container when a message arrives for that queue or topic. The following are the features of a Message-driven bean: • It is anonymous; that is, it has no client visibility. No state is maintained for the client. • All instances of a particular Message-driven bean are equivalent. • The container can pool instances. • It does not have a local or remote interface. • It is invoked asynchronously by the container. • The bean lives entirely within a container; the container manages its lifecycle and environment. These features are discussed in more detail next. The Message Producer’s View To the client producing JMS messages, the Message-driven bean is just an anonymous message consumer. The client need not be aware that the consumer is a Message-driven 430 Day 10 13 0672323842 CH10 3/20/02 9:28 AM Page 430 Message-Driven Beans 431 10 bean. The client simply sends its messages to a destination, either a queue or a topic, and the bean handles the message when it arrives. Therefore, the coding of message produc- ers in an application using Message-driven beans is exactly the same as any JMS applica- tion; that is, the message must conform to the JMS specification and the destination must be a Java Naming and Directory Interface (JNDI) registered name. Apart from this, the message does not have to correspond to any particular format. It is not necessary for the client to be a Java client application or an EJB to take advan- tage of Message-driven beans; it can be a Java ServerPages TM (JSP) component or a non-J2EE application. Similarities and Differences with Other EJBs In some respects, a Message-driven bean is similar to a stateless Session bean. It is a complete EJB that can encapsulate business logic. An advantage is that the container is responsible for providing functionality for security, concurrency, transactions, and so forth. Like a Session or Entity bean, a Message-driven bean has a bean class and XML deployment descriptor. The main difference from the other EJBs is that a Message-driven bean cannot be called directly by the client. For this reason, they do not have Home, Remote, or Local interfaces, this makes them less prone to misuse by the client. Unlike Entity and Session beans, Message-driven beans do not have a passive state. Therefore, they do not implement the ejbActivate() and ejbPassivate() methods. Although a Message-driven bean is considered to be a stateless object, from the client’s view, it can and should retain state in its instance variables. Examples of this are an open database connection and the Home, Local, and Remote interfaces to other EJBs. Note Programming Interfaces in a Message-Driven Bean There are a number of constraints on the contents of a Message-driven bean class. In par- ticular your Message-driven bean class must • Implement the javax.ejb.MessageDrivenBean interface • Implement the javax.jms.MessageListener interface 13 0672323842 CH10 3/20/02 9:28 AM Page 431 • Have a single constructor, with no arguments • Have a single public setMessageDrivenContext(MessageDrivenContext ctx) method that returns a void • Have a single public ejbCreate() method with no arguments that returns a void • Have a single public ejbRemove() method with no arguments that returns a void • Have a single public onMessage(Message message) method that returns a void • Not have a finalize() method The following sections cover these methods in more detail. Life Cycle of a Message-Driven Bean The EJB container controls the lifecycle of a Message-driven bean. The Message-driven bean instance lifecycle has three states, as shown in Figure 10.1: • Does Not Exist—The Message-driven bean has not been instantiated yet or is awaiting garbage collection. • Method Ready Pool—A pool of Message-driven bean instances, similar to the instance pool used for stateless session beans. • Processing a message—The Message-driven bean has been instantiated and is han- dling a message. 432 Day 10 F IGURE 10.1 The Message-driven bean life cycle. Message-driven Bean setMessageDrivenContext() ejbCreate() State: in Method Ready Pool do/process message onMessage() 13 0672323842 CH10 3/20/02 9:28 AM Page 432 Message-Driven Beans 433 10 After constructing the new instance of the Message-driven bean object, the container invokes the following methods: • The bean’s setMessageDrivenContext() method with a reference to its EJB con- text. The Message-driven bean should store its MessageDrivenContext reference in an instance field. • The bean’s ejbCreate() method. The Message-driven bean’s ejbCreate() method takes no arguments and is invoked only once when the bean is first instantiated. The Message-Driven Bean Context The javax.ejb.MessageDrivenContext interface (see the class diagram in Figure 10.2). provides the Message-driven bean with access to its runtime context. This is similar to the SessionContext and EntityContext interfaces for Session and Entity beans. F IGURE 10.2 The MessageDrivenContext class diagram. EJBContext MessageDrivenContext getCallerPrincipal() getEJBHome() getEJBLocalHome() getRollbackOnly() getUserTransaction() isCallerInRole() setRollbackOnly() Note that all the EJBContext methods are available to a Message-driven bean, but because the Message-driven bean does not have a local or remote interface, calls to getEJBHome() and getEJBLocalHome() will throw a java.lang.IllegalStateException. Because Message-driven beans are anonymous and run within the context of the contain- er, and the container does not have a client security identity or role, calls to the getCallerPrincipal() and IsCallerInRole() methods will also cause an IllegalStateException. 13 0672323842 CH10 3/20/02 9:28 AM Page 433 Creating a Message-Driven Bean The setMessageDrivenContext() method can throw EJBException if there is a contain- er or system level error of some kind. See the section called “Handling Exceptions” for more details. What follows is an example setMessageDrivenContext() method that saves its EJBContext and JNDI context: private MessageDrivenContext mdbContext; private Context jndiContext; public void setMessageDrivenContext (MessageDrivenContext ctx) { mdbContext = ctx; try { jndiContext = new InitialContext(); } catch (NamingException nameEx) { throw new EJBException(nameEx); } } After calling setMessageDrivenContext(), the container calls the bean’s ejbCreate() method, which takes no parameters. You could use this method to allocate resources, such as a datasource, but in practice, this is usually done in the setMessageDrivenContext() method. Therefore, it is normal to find the ejbCreate() method empty. This method is only invoked when the bean instance is first created. public void ejbCreate () throws CreateException After the ejbCreate() method has been called, the bean is placed in the method-ready pool. Method-Ready Pool The actual point at which Message-driven bean instances are created and placed in the method-ready pool is vendor specific. The vendor of an EJB server could design it to only create Message-driven bean instances when they are required. Alternatively, when the EJB server is started, a number of instances may be placed in the method-ready pool awaiting the first message. Additional instances can be added to the pool when the num- ber of Message-driven beans is insufficient to handle the number of incoming messages. Therefore, the life of a Message-driven bean instance could be very long and, in this case, it makes sense to adopt an approach where you retain state (such as an open data- base connection) across the handling of several messages. However, the container may create and destroy instances to service every incoming message. If this is the case, this approach is no longer efficient. Check your vendor’s documentation for details on how your EJB server handles Message-driven bean instances in the method-ready pool. 434 Day 10 13 0672323842 CH10 3/20/02 9:28 AM Page 434 Message-Driven Beans 435 10 Message-driven bean instances in the method-ready pool are available to consume incoming messages. Any available instance can be allocated to a message and, while pro- cessing the message, this particular bean instance is not available to consume other mes- sages. A container can handle several messages concurrently by using a separate instance of the message bean for each message. Each separate instance obtains its own MessageDrivenContext from the container. After the message has been processed, the instance is available to consume other messages. Message-driven beans are always single-threaded objects. The Demise of the Bean When the server decides to reduce the total size of the method-ready pool, a bean instance is removed from the pool and becomes available for garbage collection. At this point, the bean’s ejbRemove() method is called. You should use this method to close or deallocate resources stored in instance variables and set the instance variable to null. public void ejbRemove() The EJBException can be thrown by ejbRemove() to indicate a system-level error. Following ejbRemove(), the bean is dereferenced and no longer available to handle mes- sages. It will eventually be garbage collected. The ejbRemove() method may not be called if the Message-driven bean instance throws an exception. This could result in resource leaks. Note A Message-driven bean must not define the finalize method to free up resources: do all the tidying up in ejbRemove(). Consuming Messages When a message is received, the container finds a Message-driven bean instance that is registered for that queue or topic and calls the bean’s onMessage() method. public void onMessage(Message message) This method has a single parameter that contains a single JMS message. The message will have a header, one or more properties (optional), and a message body (consisting of one of the five JMS message body types). JMS messages were covered in some detail on Day 9. 13 0672323842 CH10 3/20/02 9:28 AM Page 435 The Message-driven bean must provide a single onMessage() method, and this method should not throw runtime exceptions. It must not have a throws clause as part of its method signature. The onMessage() holds the business logic of the bean. You can use helper methods and other EJBs to process the message. Remember, Message-driven bean instances are triggered asynchronously; the business logic within the bean must reflect this. You must never presume any ordering to the mes- sages received. Even if the system is implemented within the same JVM, the system vagaries can cause the scheduling of bean instances to be non-deterministic, this means that you cannot ascertain or control when the bean will run. Handling Exceptions The Message-driven bean can encounter various exceptions or errors that prevent it from successfully completing. The following are examples of such exceptions: • Failure to obtain a database connection • A JNDI naming exception • A RemoteException from invocation of another EJB • An unexpected RuntimeException A well-written Message-driven bean should never carelessly throw a RunTimeException. If a RunTimeException is not caught in onMessage() or any other bean class method, the container will simply discard the instance (it will transition it to the Does Not Exist state). In this case, the container will not call the ejbRemove() method, so a badly writ- ten bean method could cause resource leaks. Obviously, you need a mechanism to tell the container that you have caught an unrecov- erable error and die gracefully. To do this, you use exception layering. You catch the RunTimeException, free up resources, do any other appropriate processing and then throw an EJBException to the container. The container will then log the error, rollback any container-managed transactions, and discard the instance. Because identical Message-driven bean instances are available, from the client perspec- tive, the message bean continues to exist in the method-ready pool to handle further mes- sages. Therefore, a single instance failure may not cause any disruption to the system. Container- and Bean-Managed Transactions The analysis of container- versus bean-managed transactions was covered as part of Day 8’s material, reread this if you need to recap the benefits of either method of handling transactions. When designing your Message-driven bean, you must decide whether the 436 Day 10 13 0672323842 CH10 3/20/02 9:28 AM Page 436 [...]... job.getSkills(); 45: int skillMatch = 0; 46: Iterator jobSkillIter = jobSkills.iterator(); 47: while (jobSkillIter.hasNext()) { 13 0672323842 CH10 3/20/02 9:28 AM Page 455 Message-Driven Beans LISTING 10 .5 455 Continued 48: SkillLocal jobSkill = (SkillLocal)jobSkillIter.next(); 49: if (appSkills.contains(jobSkill.getName())) 50 : skillMatch++; 51 : } 52 : if (jobSkills.size() > 0 && skillMatch == 0) 53 : continue; 54 :... topic), do the following: 1 Ensure that J2EE server is running 2 Use j2eeadmin or deploytool to create the message queue or a topic To see the existing queues and topics, use the following: j2eeadmin –listJMSDestination or view the Destinations screen in deploytool This is found by selecting Server Configuration from the Tools menu and then the Destinations icon in the left panel The J2EE RI has two default... 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50 : 51 : 52 : Continued private QueueSender queueSender; private Queue queue; private static final String jndiFactory = “QueueConnectionFactory”; private static final String jndiQueue = “jms/firstQueue”; public static void main(String[] args) { try { PTPSender sender = new PTPSender(jndiFactory, jndiQueue); System.out.println (“Sending message... 450 Page 450 Day 10 catch (JMSException e) { error(“Error sending applicant details to message ➥ bean “+login,e); } 3 In the RegisterBean.java file, change updateDetails() to send a message to indicate that the applicant’s details have changed The added lines are shown in bold in the following code public void updateDetails (String name, String email, ➥ String locationName, String summary, String[] skillNames)... can add the queue in deploytool on the Destinations screen FIGURE 10 .5 Using j2eeadmin to add a JMS queue to the container When your bean is deployed, the following will appear in the XML deployment descriptor 10 13 0672323842 CH10 3/20/02 9:28 AM Page 442 442 Day 10 javax.jms.Queue Deploying the Message-Driven... line is shown in bold in the following code public void createApplicant(String login, String name, ➥ String email) throws DuplicateException, CreateException{ try { ApplicantLocal applicant = ➥ applicantHome.create(login,name,email); messageSender.sendApplicant(applicant.getLogin(),true); } catch (CreateException e) { error(“Error adding applicant “+login,e); } 13 0672323842 CH10 3/20/02 9:28 AM 450 ... default queues predefined—jms/Queue and jms/Topic To add your queue, use the following: j2eeadmin –addJMSDestination jms/firstQueue queue The j2eeadmin command works silently, so to check that your queue has been created, run J2eeadmin -listJMSDestination once more Figure 10 .5 demonstrates the use of these two commands to create a queue called jms/firstQueue (this is the queue you will use in this first example)... the bean apart from the exception handling The full listing of the ApplicantMatch Message-driven bean is shown in Listing 10 .5 10 13 0672323842 CH10 3/20/02 454 9:28 AM Page 454 Day 10 LISTING 10 .5 Full Listing on ApplicantMatch.java Message-Driven Bean Code 1: package data; 2: 3: import java.util.*; 4: import javax.ejb.*; 5: import javax.jms.*; 6: import javax.naming.*; 7: 8: public class ApplicantMatch... Page 4 45 Message-Driven Beans LISTING 10.2 Continued 6: 7: 8: MDBPrintMessage 9: MDBPrintMessage 10: MDBPrintMessage 11: Container 12: 13: javax.jms.Queue 14: 15: ... void error (String msg, Exception ex) { 1 15: String s = “ApplicantMatch: “+msg + “\n” + ex; 116: System.out.println(s); 117: throw new EJBException(s,ex); 118: } Compile this bean Step 4—Create the JMS Queue Run the J2EE RI and use j2eeadmin to create the JMS queue j2eeadmin –addJMSDestination jms/applicantQueue queue Alternatively, use deploytool and select Destinations from the Configure Installation . message in the point-to-point domain? A Messages in the point-to-point domain are PERSISTENT by default and will be delivered unless they have a timeout that has expired. Point-to-point messages. is running. 2. Use j2eeadmin or deploytool to create the message queue or a topic. To see the existing queues and topics, use the following: j2eeadmin –listJMSDestination or view the Destinations. use the following: j2eeadmin –addJMSDestination jms/firstQueue queue The j2eeadmin command works silently, so to check that your queue has been created, run J2eeadmin -listJMSDestination once