Expert one-on-one J2EE Design and Development phần 7 ppt

69 371 0
Expert one-on-one J2EE Design and Development phần 7 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

The AbstractLocalStatelessSessionServiceLocator subclass implements the located( ) template method to check that that the object is actually an EJB local home, and to invoke an abstract setEjbHome( ) method that must be implemented by subclasses that know the application-specific type of the EJB home, enabling them to obtain EJB references from it: 420 Brought to you by ownSky Infrastructure and Application Implementation It's always safe to cache a reference to a local EJB home interface. Although the EJB specification (§6.2.1) implies that it's safe to cache a remote home, there is a slight chance that a cached remote home reference may become "stale" in some servers (for example, if the remote server hosting the EJB was restarted during the client's lifetime). The following UML class diagram illustrates the superclasses for both local and remote SLSB service locators, and how a LocalSLSBBoxOf f iceFactory class might be used in the sample application to create BoxOffice objects (actually EJBs) as required: Let's look at how these superclasses can be used to simplify the implementation of a real service locator. The BoxOff iceFactory interface is the public interface of our application-specific typed service locator: public interface BoxOfficeFactory { BoxOffice getBoxOffice() throws FatalException; } Extending the com.interface21.ejb.access.AbstractionLocalStatelessSessionServiceLocator superclass makes it trivial to implement this interface: public class LocalSLSBBoxOfflceFactory extends AbstractLocalStatelessSessionServiceLocator implements BoxOf ficeFactory { The implementation of the protected abstraction setEjbHome() method caches the home interface, after casting it to the appropriate type. This method will be invoked once, when the service locator is initialized: Private BoxOfficeHomehome; protected void setEjbHome(EJBLocalHome home) { this.home = (BoxOfficeHome) home; } 421 Brought to you by ownSky The implementation of the getBoxOffice( ) method from the BoxOfficeFactory interface, which will be invoked by code using the EJB, creates a new SLSB reference as required. EJBCreate exceptions are caught and an unchecked exception, FatalException, is rethrown: The following bean definition shows how such a service locator could be defined in the sample application's bean factory. The only property that must be set is jndiName, inherited from the AbstractJndiLocator superclass: <bean name="boxOfficeFactory" class="com.wrox.expertj2ee.ticket.framework.ejb.LocalSLSBBoxOfficeFactory" > <property name=" jndiName">ejb/BoxOf f ice</property> </bean> This is a simple approach that works very well with local EJBs. It hides EJB access from code that uses the business interface. One disadvantage is that it requires a factory interface and an implementing class for each EJB. This isn't usually a major problem, as with sensible design few systems need a vast number of EJBs. Slightly more seriously, all code that uses the EJB must work with the factory object: it's impossible to hold on to a reference to the business interface. Transparent Dynamic Proxy: An Alternative to the Typed Service Locator for Local EJB Access An alternative to the Service Locator pattern that achieves the same end is to conceal the factory in a custom bean definition, as described when we discussed our "bean factory" approach above. The custom bean definition looks up and caches the bean's home interface before returning an object of the required interface, which is actually a dynamic proxy wrapping a new instance of the SLSB. Before each method invocation, the dynamic proxy - which is threadsafe - transparently creates a new instance of the SLSB. Although the custom bean definition doesn't know the type of the EJB, it knows that an SLSB local interface must expose a create ( ) method without arguments, which it can use a BeanWrapper object to invoke by name. Benchmarks show that this approach has only a small performance overhead. This use of dynamic proxies and reflective method invocation sounds complex, but the complexity is hidden within framework code; it enables us to write less application code. Applications need only define a bean using the custom bean definition, as follows. Note the specification of theJNDI name and business interface: <bean name="boxOffice" definitionClass= "com.interface21.ejb.access.LocalstatelessSessionProxyBeanDefinit ion "> <customDef inition property="business Interf ace"> com.wrox.expertj2ee.ticket.boxof f ice.BoxOffice </customDefinition> 422 Brought to you by ownSky Infrastructure and Application Implementation <customDefinition property="jndiName"> ejb/BoxOffice </customDefinition> </bean> That's it: calling code can simply obtain a BoxOff ice object like this, and cache it: BoxOffice boxOffice = (BoxOffice) beanFactory.getBean("boxOffice"); Most likely, this won't even be necessary: objects that need to use the BoxOffice interface can simply expose a bean property and have the application context set it to the boxOffice reference at run time. This approach has the advantage of requiring no custom code to access an EJB. We need only define a bean using the custom definition. This is the approach used in the sample application, although it would be simple to switch to the Typed Service Locator approach, code for which is included in the download. See the code in the com. interface21. ejb. access. LocalStatelessSessionProxyBeanDef inition class for the implementation of this approach. Using the Business Delegate Pattern for Remote EJB Access A higher level of decoupling is achieved by the Business Delegate pattern, in which a client-side object exposes business methods that are implemented by calls to the EJB. This is more of a facade than a proxy approach, as the business delegate may change method signatures - for example, changing exception types or combining method calls, or even invoking multiple EJBs. As the Business Delegate pattern requires more work to implement than the Service Locator, I'll focus on its use with remote interfaces, with which it delivers real value (its benefits are reduced with local EJB access). The benefits of a business delegate approach include: o The business delegate may be able to retry failed transactions if this is indicated, without calling code being complicated. o It's possible to handle exceptions from the server in a consistent way without every client needing to catch J2EE infrastructure-related exceptions such as java.rmi.RemoteException and javax.naming.NamingException. The business delegate can catch such low-level exceptions, log them and throw an application-specific exception that makes more sense to client code. Or it may be appropriate to wrap EJB-tier exceptions as unchecked exceptions to simplify client code. This makes sense when exceptions are fatal - for example, if we've established that there's no point retrying after encountering a remote exception that prevents execution of a use case. o It may be possible to implement operations at a higher level of abstraction. For example, if a particular use case requires multiple EJB invocations, these may be able to be performed in the business delegate. o It may be possible to introduce caching by providing a caching implementation of the business delegate interface. This will benefit all clients. 423 Brought to you by ownSky The drawback of using a business delegate is the potential duplication of the business interface exposed by the EJB tier on the client-side. However, using a business delegate offers an opportunity to provide an interface that best fits the needs of clients. For example, we can simplify the interface exposed by the EJB tier by omitting irrelevant operations. We can simplify client code by only throwing checked exceptions if they're part of the API, and the client can be expected to handle them. It's superficially attractive for the business delegate to implement the session bean's business methods interface. However, this fails to decouple EJB tier and client tier, and cannot deliver some of the benefits we've identified, such as concealing remote exceptions and simplifying the client API. Hence, I don't recommend copying method signatures in the business delegate (the approach recommended in EJB Design Pattern. If this is appropriate, the Service Locator pattern is simpler and should be preferred. Our framework provides easy support for implementing business delegates. The same superclass we used for the typed service locator, AbstractLocalStatelessSessionServiceLocator, can be used as a superclass for a business delegate. Instead of merely returning an instance of the EJB on demand, a subclass would implement business operations, calling the EJB as necessary. As the sample application uses EJBs with local interfaces, there is no need to use the Business Delegate pattern, which requires more work to implement than a service locator. The following example illustrates the use of a business delegate to access a remote EJB. It uses a hypothetical EJB with a remote interface that exposes two int values: one of which can be cached, and one of which cannot. We saw this EJB's business methods interface and component interface under Implementing EJBs above. The remote interface exposes the following business methods: int getUncacheableValue() throws RemoteException; int getCacheableValue() throws RemoteException; By using the Business Delegate pattern, we can simplify calling code by hiding remote exceptions and rethrowing unchecked fatal exceptions, and improve performance by caching the cacheable data value for a given period. When using the Business Delegate pattern, it is good practice to define a business delegate interface, which application code will work with. This enables a caching implementation to be substituted without impact on client code, for example. The interface might look as follows for this EJB: public interface MyBusinessDelegate { int getUncacheableValue() throws FatalException; int getCacheableValue() throws FatalException; } The following implementation extends the AbstractRemoteStatelessSessionServiceLocator framework superclass, which performs the necessary JNDI lookup and provides it with an EJB home it can cache in the setEjbHome() method: public class MyBusinessDelegatelmpl extends AbstractRemoteStatelessSessionServiceLocator implements MyBusinessDelegate { private static long MAX_DATA_AGE = 10000L; private CalculatorHome home; 424 Brought to you by ownSky Infrastructure and Application Implementation It's important to abstract access to EJBs. With EJBs with local interfaces, use the Service Locator pattern, or the Business Delegate if there is a good reason that calling code shouldn't work with the EJB interface. With EJBs with remote interfaces, use the Business Delegate pattern, and conceal remote access details from clients. Using JMS Like JDBC, JMS is a complex API. Working with it directly can obscure the purpose of application code. Thus an abstraction layer is appropriate. The com.interface21.jms.JmsTemplate class applies the same approach as the JdbcTemplate we examined in Chapter 9. We should try to conceal in framework code the complexity of the necessary JNDI lookups and the requirement to work with multiple JMS API objects. 425 Brought to you by ownSky Let's consider com.interface21.jms.JmsTemplate class's support for Pub/Sub messaging. On startup, a JmsTemplate instance does a JNDI lookup for the TopicConnectionFactory. The JNDI name is passed into the constructor, as it may differ between application servers. The following method uses the cached TopicConnectionFactory to invoke a callback interface to create a message given a TopicSession, which it then publishes: The com. interface21.jms.JmsException exception is unchecked, meaning that we have a. choice as to whether to catch it. Calling code can implement the PubSubMessageCreator inner interface of JmsTemplate (shown below) without deep knowledge of JNDI: public interface PubSubMessageCreator { Message createMessage (TopicSession topicSession) throws JHSException; } 426 Brought to you by ownSky Infrastructure and Application Implementation Thus a message can be published with the following code: Message consumers can also benefit from a simplifying infrastructure. Note that it's often best to use MDBs as message consumers, as the EJB container provides valuable infrastructure for JMS message consumption; however, we can't always use EJB. We always need to implement the MessageListener interface. However, we don't want to have to deal with the complexity of registering a listener with a topic, which also involves multiple JNDI lookups. The JmsTemplate class provides a convenience method for this, with the following signature: public TopicConnection subscribeToTopicNonDurable (String topicName, MessageListener listener, String messageSelector) throws Jms Except ion; A custom bean definition of class com.interface21.jms.JmsListenerBeanDefinition can automatically register any JMS listener to a given topic. The following example is from the sample application, in which the com.wrox.expertj2ee.ticket.referencedata.support.DataUpdateJmsListener JMS listener notifies caching objects of a data update: <bean name="referenceDataListener" def initionClass="com.interface21.jms.JmsListenerBeanDef inition "> <customDefinition property="listenerBean"> com.wrox.expert j2ee.ticket.referencedata.support.DataUpdateJmsListener </customDef inition> <customDefinition property="topicConnectionFactoryName"> jms/TopicFactory </customDef inition> <customDefinition property="topicName"> jms /topic/referencedata </customDef inition> <property name="cachingCalendar" beanRef =" true"> calendar </property> </bean> This enables us to implement a simple JMS listener that responds to events and refreshes data cached in another application component without the need to write any low-level JMS code, as shown in the following listing: 427 Brought to you by ownSky This simple abstraction doesn't take care of all JMS requirements - the JMS API includes many features such as transacted sessions, which pose more problems for abstraction. However, it provides a simple infrastructure for the commonest needs. Implementing Business Logic As a professional developer, you know how to program in Java. What I want to discuss here is how to put an application together and how to focus your programming skills on your application's domain. Thus the following discussion of the sample application does not address low-level details such as the implementation of the adjacent seating algorithm, but focuses on how the sample application's components are configured and work together. We'll look both at how the sample application uses standard J2EE infrastructure and how it uses the infrastructure we've discussed so far in this chapter. The sample application's code is available in the download accompanying this book. Please refer to it a necessary during the following discussion, as it's impractical to provide a complete listing of all the classes mentioned. Implementing the Sample Application Let's now consider some of the key business logic of the sample application. Let's focus on two key issues: how we obtain reference data about genres, shows, and performances; and how we obtain information about seat availability and implement the booking process. These use cases are described in detail in Chapter 5, so I'll just provide a quick summary of the requirement here, along with the high-level design decisions taken in the last few chapters concerning them. 428 Brought to you by ownSky Infrastructure and Application Implementation Genre, show, and performance data changes rarely and can be shared between all users of the application. The retrieval of this data need not be transactional. The booking process requires transactional data access, and involves the holding of user session state. We have chosen to hold user session state in an HttpSession object in the web container. Defining Business Interfaces The first step is to define the necessary business interfaces. These will not be web-specific; however, they will run in the web container in a collocated application. These interfaces should be able to be implemented with or without EJB without affecting code that uses them. In a collocated application such as our sample application, we'll use the Service Locator pattern, discussed above, to hide the details of EJB access completely. There is only one area in which retaining the option of using EJB affects our design: exception handling. As EJBs handle unchecked exceptions as fatal system exceptions, as we saw in Chapter 10, we are unable to use unchecked exceptions in a business interface that might be implemented by an EJB. Occasionally this calls for compromises. Consider the case of a request to find the number of seats free for a performance, given a performance ID that doesn't match any performance. Such a request cannot occur in our application, as the user can only request availability using links from pages that will provide valid performance IDs. Thus it makes no sense to complicate application code by catching a checked NoSuchPerformanceException. However, given that we would like the ability to implement our BoxOffice interface using EJB, we must choose the lesser of the following two evils: make all our application exceptions (including NoSuchPerformanceException) checked and cope with the overhead of catching checked exceptions where this serves no useful purpose; or lose the ability to switch between EJBs and other business object implementations transparently. In the present application we opt for the use of checked exceptions. We could use the Business Delegate pattern, to catch checked exceptions from EJBs and rethrow unchecked exceptions. However, this requires more work to implement. In the sample application, we will want two separate interfaces to handle the functionality we're considering here: one to deal with reference data, and one to deal with availability and the booking process. Let's consider each in turn. The com.wrox.expertj2ee.ticket.referencedata. Calendar interface handles reference data requirements: public interface Calendar { List getCurrentGenres(); Show getShowtint(id) throws ReferenceDataException; Performance getPerformance(int id) throws ReferenceDataException; } The Genre, Show, and Performance objects are lightweight value objects, disconnected from the data source. They expose a tree structure, navigable downwards: from a genre we can find shows, and from each show we can get a list of performances. The entire tree can be cacheable for up to one minute, or as long as we know that there has been no change to reference data (the complexity of caching objects individually is unwarranted, as it would deliver little benefit). 429 Brought to you by ownSky [...]... very important topic A poorly designed web interface is one of the quickest paths to an unmaintainable application and overall project failure Experience with poorly designed web interfaces that proved hugely expensive in development effort and business opportunity first made me passionate about J2EE design After surveying the problems we seek to avoid in web application design, we'll focus on the MVC... the Observer design pattern, provides a standard way to look up messages to meet internationalization requirements, and allows application components to communicate as necessary at run time The standard J2EE infrastructure makes some simple things difficult, which has caused widespread frustration withJ2EE in practice This can be addressed by a simplifying infrastructure that makes J2E APIs and services... discussed the importance and benefits of a strong generic infrastructure backing application implementation While a J2EE application server provides powerful standard infrastructure forJ2EE applications, we need to use additional infrastructure components to make the J2EE APIs easier to use, ensure that application code focuses on addressing business problems, and solve problems that the J2EE specifications... from request to command bean properties This may free application code from the need to handle request parameters o The command is not dependent on the Servlet API, so can be passed to business objects This promotes clean interaction between servlets and business objects o The command approach works well with the Observer design pattern; it's easy to publish events on receipt of commands The disadvantages... important of J2EE web technologies I'll use the term Front Controller pattern, which is used by Core J2EE Patterns This is an attempt to apply the MVC architectural pattern (which we'll discuss further below) to web applications This pattern, which we'll discuss in detail below, is the key to designing maintainable J2EE web applications, and the central point of this chapter Both servlets and view technologies... as JSP) are required for building maintainable J2EE web applications Servlets and helper classes should be used to handle control flow and initiate business operations; JSP should only be used to render content Web-Tier Design Goals Enough negatives: what features should a good web interface have? The following two goals are central to maintainability and extensibility A Clean Web Tier As JSP Model... Cause the Creation of a Command? A common approach is for a request to result in the generation of a Command object: a business object containing data associated with the request, but not dependent on the Servlet API This approach has the following advantages: o We can enjoy the advantages of the Command design pattern, such as the ability to queue, log, and possibly undo commands o We may be able to... problem domain and implementing business logic Finally, we looked at some of the core implementation of the sample application, illustrating how it is simplified through use of appropriate infrastructure 439 Brought to you by ownSky Web-Tier MVC Design In this chapter we'll look at web-tier design and how to ensure that a web interface is a simple and maintainable layer built on business objects and overall... business value in using the Command design pattern In the latter case, the use of the Command design pattern should be driven by the relevant business interfaces; it is for business objects, not web interface, to determine the design patterns that will be used in intra-application communication Implementation Goals Using a web application framework should simplify application code and promote clean interaction... languages such as C or Perl, which usually caused a new process to be created to handle each request, servlets allowed dynamic content to be generated by reusable Java objects Servlets had full access to the power of the Java language, including JDBC and the Java object model, and so elevated web development to true OO development At least in theory - the Servlet specification was a big step forward, . reference data, and one to deal with availability and the booking process. Let's consider each in turn. The com.wrox.expertj2ee.ticket.referencedata. Calendar interface handles reference. application's components are configured and work together. We'll look both at how the sample application uses standard J2EE infrastructure and how it uses the infrastructure we've. singleton="false" class="com.wrox.expertj2ee.ticket.referencedata.jdbc.JdbcCalendar"> </bean> <bean name="calendar" class="com.wrox.expertj2ee.ticket.referencedata.support.CachingCalendar">

Ngày đăng: 13/08/2014, 12:21

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan