Art of Java Web Development STRUTS, TAPESTRY, COMMONS, VELOCITY, JUNIT, AXIS, COCOON, INTERNETBEANS, WEBWORK phần 9 ppt

62 239 0
Art of Java Web Development STRUTS, TAPESTRY, COMMONS, VELOCITY, JUNIT, AXIS, COCOON, INTERNETBEANS, WEBWORK phần 9 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

466 CHAPTER 15 Resource management boundaries are stored in both the pool and the session by their class names, which are unique because they include the package name. Because of the way packages in Java must correspond to physical locations, it is impossible to have two classes with the same fully qualified name (because you can’t have two files with the same name in a directory). If the method can’t retrieve a boundary from the session, it goes to the object pool to get the boundary. As you know from the discussion in chapter 14, section 14.3.3 (particularly listing 14.10), the Commons pool relies on a class that implements the Keyed- PoolableObjectFactory interface to return the appropriate object from the pool. The pool uses this class to construct and name the objects that it pools. To pool and store the boundaries by their class names, we use the KeyedBoundaryPoolFac- tory class shown in listing 15.17. package com.nealford.art.facade.emotherearth.util; import org.apache.commons.pool.KeyedPoolableObjectFactory; import com.nealford.art.facade.emotherearth.boundary.ProductDb; import com.nealford.art.facade.emotherearth.boundary.OrderDb; public class KeyedBoundaryPoolFactory implements KeyedPoolableObjectFactory { public Object makeObject(Object key) { if (key.equals(com.nealford.art.facade.emotherearth. boundary.ProductDb.class)) { return new ProductDb(); } else if (key.equals(com.nealford.art.facade.emotherearth. boundary.OrderDb.class)) { return new OrderDb(); } else return null; } public void destroyObject(Object key, Object obj) { } public boolean validateObject(Object key, Object obj) { return true; } public void activateObject(Object key, Object obj) { } public void passivateObject(Object key, Object obj) { } } Listing 15.17 The KeyedBoundaryPoolFactory populates the object pool. Caching strategies 467 You must exercise caution when caching objects in both an object pool and the user's session. If you have 60 objects in the pool with session caching enabled, and you have 60 concurrent users, each user will have an instance from the pool checked out, residing in his or her session cache, which means no objects are left in the pool. You can solve this problem by turning off session caching (which slows down each user’s interaction slightly because the boundary is no longer cached in the user’s session) but lowering the memory requirements for the entire application and supporting more concurrent users. You can also solve this problem by creating pools that automatically grow so that you never run out of boundary objects from the pool. Design decisions pitting users’ response time against scalability frequently arise in web applications. The Cacheable interface One other item of note in the borrowBoundary() method is the use of an interface named Cacheable . This is a tagging interface, built as part of the application, that serves as a flag to indicate which boundary classes we want to cache in the applica- tion. This interface appears in listing 15.18. package com.nealford.art.facade.emotherearth.util; public interface Cacheable { } The Cacheable interface allows a developer to flag a particular boundary class as one that should be cached. Because it contains no methods, its use is restricted to acting as a discriminator via the instanceof operator. To specify that a class is cached, the definition of the class should implement this interface: public class ProductDb extends BoundaryBase implements Cacheable { The borrowBoundary() method tests both the deployment flag from the servlet context and the presence of this interface via instanceof to determine if a bound- ary is placed in the session when it is borrowed from the pool. Generically returning boundaries We’ve also modified the returnBoundaries() method to accommodate the generic boundary façade. The new method appears in listing 15.19. Listing 15.18 Cacheable allows a boundary to specify that it should be cached. 468 CHAPTER 15 Resource management public void returnBoundaries(HttpSession session, boolean preserveCachedBoundaries) { GenericKeyedObjectPool boundaryPool = (GenericKeyedObjectPool) session.getServletContext(). getAttribute(BOUNDARY_POOL); boolean cacheInBoundaryInSession = Boolean.valueOf(session.getServletContext(). getInitParameter(CACHE_BOUNDARY_IN_SESSION)). booleanValue(); Iterator borrowedObject = borrowedObjects.iterator(); while (borrowedObject.hasNext()) { Object o = borrowedObject.next(); if (o instanceof BoundaryBase) if (cacheInBoundaryInSession && preserveCachedBoundaries && o instanceof Cacheable) break; else { try { boundaryPool.returnObject(o.getClass(), o); } catch (Exception x) { session.getServletContext().log( "Pool return exception: " + x.getMessage()); } finally { borrowedObject.remove(); } } } } One of the changes required in the generic façade class is the class-level member variable that keeps track of all the objects referenced by the façade so that it can gracefully remove them. The returnBoundaries() method receives two parame- ters: the user’s session and a Boolean flag indicating whether the cached bound- ary objects should be preserved. The method uses the session to gather session- and context-level variables, and then iterates over the list of borrowed objects, conditionally returning them based on the cache settings, whether the caller wants the caches preserved, and the cacheability of the object. Once the objects are returned to the pool, they are removed from the list. The list of borrowed objects is not necessary if the objects are cached in the ses- sion. However, not all boundaries will be cached in the session, so an additional Listing 15.19 The returnBoundaries() method Caching strategies 469 reference is kept within the façade to ensure the successful return of the pooled objects. The other change to the application for the generic façade appears in the Ses- sionAttributeListener , which is also simplified. The attributeRemoved() method shrinks to a mere two lines: public void attributeRemoved(HttpSessionBindingEvent se) { BoundaryFacade facade = BoundaryFacade.getInstance(); facade.returnBoundaries(se.getSession(), false); } You never want the boundaries preserved in the cache, so the Boolean parameter is passed as false to ensure that all the objects are returned. This method serves as insurance in case the user abandons the application and allows the session to time out. The result for all the classes that use the façade is even greater simplification. For example, each controller that needs a boundary can call the façade with the appropriate class and call returnBoundaries() when their work is completed. All the intelligence about caching, pooling, and other management issues is strictly encapsulated inside the façade. 15.1.3 Resource management in frameworks Both design patterns introduced in this chapter work well with the Model 2 frame- works in part 2. As in the last chapter, these strategies pertain mostly to the bound- ary and entity classes, leaving the infrastructure to the framework. The only framework that might not benefit from these patterns is Tapestry, simply because much of the object pooling and caching is built into that framework. Notice the trend in the best practices chapters thus far. Once you have a clean Model 2 application, either with or without a framework in place, the main cod- ing focus for resource management and optimization moves to the boundary and entity layer. Frameworks are built to manage plumbing for you, freeing you to focus your coding efforts on the parts of the application where you can have the most impact. This is not an accident, but the result of using a well-thought-out architecture like Model 2. You will also notice that the framework where most of these optimizations aren’t possible is the non-Model 2 framework, InternetBeans Express. While quicker to build in the initial case, the RAD framework imposes so many restrictions that it ends up not being flexible enough. 470 CHAPTER 15 Resource management 15.2 Other resources you need to manage The chapter has so far concentrated on memory usage; we’ve discussed several design patterns that support caching strategies. Memory is perhaps the most important resource you must manage. However, it isn’t the only one. You must also be aware of other external resources, such as the JNDI lookup context for application servers, as well as internal resources, such as the various collections. 15.2.1 Effectively using JNDI If you use JNDI to handle database connection pooling or Enterprise JavaBeans, you must manage the connection maintained on behalf of the user. The JNDI con- nection to the application server is similar to a connection to a database for a client/server application. Both handle authentication, and each takes a relatively long time to establish. It is important to establish the connection to JNDI on behalf of the user and hold that connection. Because it holds the login to the application server, the con- nection is unique to a particular user. Thus, a connection cannot be pooled like other objects. The typical strategy when using JNDI is to establish the connection for the user at logon and place the context in the user’s session. This is illustrated in the sample application from chapter 12, section 12.3.1 (the Model 2 schedule application rewritten to use EJBs). This sample appears in the source code archive under the name art_sched_ejb. The ViewSchedule controller servlet is a good example of the effective use of JNDI; it appears in listing 15.20. package com.nealford.art.ejbsched.controller; import java.io.IOException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.nealford.art.ejbsched.model.ScheduleBean; public class ViewSchedule extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Listing 15.20 This controller establishes and holds the JNDI context for this user. Other resources you need to manage 471 Context c = establishContext(request); forwardToView(request, response, populateModel(c)); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } private void forwardToView(HttpServletRequest request, HttpServletResponse response, ScheduleBean scheduleBean) throws ServletException, IOException { request.setAttribute("scheduleBean", scheduleBean); RequestDispatcher rd = request.getRequestDispatcher( "/ScheduleView.jsp"); rd.forward(request, response); } private ScheduleBean populateModel(Context c) { ScheduleBean scheduleBean = new ScheduleBean(); scheduleBean.setContext(c); try { scheduleBean.populate(); } catch (Exception x) { getServletContext().log( "Error: ScheduleBean.populate()"); } return scheduleBean; } private Context establishContext(HttpServletRequest request) { HttpSession session = request.getSession(true); Context c = (Context) session.getAttribute("context"); if (c == null) { c = getInitialContext(); session.setAttribute("context", getInitialContext()); } return c; } private Context getInitialContext() { Context c = null; try { c = new InitialContext(); } catch (NamingException ex) { ex.printStackTrace(); } return c; } } Creates and saves the JNDI context 472 CHAPTER 15 Resource management The establishContext() method checks to see if the context has already been added to this user’s session. If not, it executes the relatively time-expensive opera- tion of establishing the context and places it in the session for future use. Generally, in applications with a resource like JNDI, the developer provides both a logon and a logout page to handle the resources. Of course, users aren’t forced to visit the logout page, so it is also a good idea to create a SessionAt- tributeListener to take care of cleaning up resources when the user’s session times out. You still waste those resources for exactly the amount of time you have set as the session timeout, but at least you are sure that the resources are eventu- ally reclaimed properly. 15.2.2 Using lazy instantiation Lazy instantiation is a resource-allocation strategy that lets you put off creating resources until they are absolutely needed. This approach works best for relatively heavyweight resources that may or may not be required for a user. For example, suppose you have an administration part of your web application that must con- nect to a variety of databases, gather a lot of records, and make web service calls, in addition to other time-consuming stuff. Because it is used only by a handful of users, you should put off building resources for it until they are necessary. This strategy runs counter to the way that most servlet engines and application servers work. One of the keys to performance is pre-creation, pooling, and cach- ing. Application servers can afford to do this because all the resources they allo- cate are generally infrastructural in nature. It is a safe bet that all web applications will need database connections, threads, and other boundary kinds of resources. Lazy instantiation is a more useful strategy for domain-level artifacts, such as administration modules or other resource-intensive items. This strategy presents you with the classic trade-off of saving resources at the expense of taking more time to deliver the resource when it is needed. 15.2.3 Working with web collections Any Java developer who has gotten this far in our book has a good understanding of the attribute collections available through the Java web APIs. The collections are well documented in numerous books. However, it seems that developers are fix- ated on the session collection as a magical repository when sometimes other col- lections are better suited. In particular, most of the code I end up seeing makes too little use of the request collection in favor of session. I bring this up because mis- use of the standard collections is a classic resource waster. You should always pick the attribute collection that has the narrowest scope that will get the job done. Summary 473 Session cops It is a good idea to establish the position of “session cop” on the development team for resource-intensive web applications. This person’s duty is to monitor all the items developers are placing in collections to ensure that the application isn’t going to be overwhelmed when it comes time to deploy it. Specifically, the session cop should verify that: ■ The correct collection is being used. ■ Collection attributes are cleaned up as quickly as possible. ■ Developers aren’t placing large data structures in memory that will harm the scalability of the application. ■ Developers have a good justification for placing an object in the collection. The session cop should warn the technical leader of the project when cross- developer contentions over collections usage arise (for example, two groups of developers absolutely must cache large chunks in the session, but the infrastruc- ture can’t handle both). 15.3 Summary The Java web APIs provide a wealth of opportunities for managing resources. Caching is an effective way to manage resources, and two design patterns offer good alternatives for creating caches. The Flyweight design pattern creates a col- lection of canonical objects, which are representatives of all objects of that type. The Façade design pattern creates a simple interface to a large number of com- plex, interrelated classes. While Façade isn’t a caching strategy per se, it is effec- tive for hiding the details of elaborate caching scenarios behind an easy-to-use façade of classes. You must manage other resources as well, such as JNDI connections and the standard collections. You should always save the JNDI context for the user rather than establishing it when needed. To handle collections correctly, choose the most suitable collection for the job at hand. Careless use of these collections can needlessly harm the scalability of the application. The establishment of a “session cop” to act as an application-wide overseer helps complex applications manage resources gracefully and lets you spot problems quickly. In chapter 16, we look at debugging and logging in web applications. 475 Debugging This chapter covers ■ Debugging web applications ■ Debugging with the SDK and IDEs ■ Logging with the SDK and log4j [...]... original JSP consists of 91 lines of code The generated servlet for the exact same page using JSTL consists of 4 19 lines! Of course, one of the benefits of using custom tags is the lack of debugging required for code that is already debugged What about the developer who writes the custom tag and must debug it during development? Now that we’ve painted an impossibly bleak picture of debugging web applications,... though the web APIs in Java effectively hide almost all the threaded nature of the servlet engine from you at development time, you must deal with it as you are debugging the application Local variables in Java exist in terms of the thread that owns them Thus, you cannot avoid threads if you need to look at the values of local variables Some debuggers in IDEs simplify this task for you Debugging web applications... look at the internals of the application, you can use the SDK debugger Also, if you work as a consultant, you generally don’t have a choice of the tools that are available Because it is part of the SDK, the jdb debugger is always there for you 16.2.1 Starting the debugger The jdb debugger documentation is provided in the JavaDocs for the SDK and reveals the basic details of how to start it and the available... consuming While it does provide most of the information that more sophisticated debuggers give you, it is harder to access because of the command-line nature of the tool However, being familiar with its use may be a lifesaver because it is the only debugger that you know for sure will always be present 16.3 Debugging with IDEs A lot of Java development, especially web development, may be done without elaborate... the debugger To start debugging a web project in NetBeans, you have to import the application into the NetBeans IDE This is a straightforward and well-documented process, so I won’t cover it here Once you’ve imported the project, create a web module that 494 CHAPTER 16 Debugging points to the document root of your project’s web application The base eMotherEarth project is the subject of debugging for... application starts To see a list of all the commands available for the debugger, enter a question mark at this prompt A small portion of this list of commands appears in figure 16.2 The first command runs the application Invoking the run command starts the servlet engine, and you will see the normal startup logging information for the servlet engine At that point, you can invoke one of the applications... variety of add-ons that you may install Some of these plug-ins enhance the web development and debugging support in NetBeans, including adding support for such tasks as debugging in JSPs NetBeans supports all the same operations as the jdb debugger, but in a much friendlier and intuitive environment The examples shown in this section assume that you’ve installed the latest web development plug-ins Starting... results of two such invocations of the dump command The first dumps the contents of the outputList variable The second invocation evaluates an expression for the contents of the first element of the list Notice that type casts, method calls, and other legal Java syntax is permitted with this command Figure 16.8 The dump command in conjunction with expressions is a powerful way to determine the contents of. .. instead of forcing you to wrestle with a command line With plug-ins available that support web development, NetBeans makes a nice environment for developing Figure 16.14 NetBeans allows the developer to set breakpoints on code in a JSP 498 CHAPTER 16 Debugging Figure 16.15 The HTTPMonitor tool shows numerous web aspects of your running application, including attributes and parameters and debugging web. .. into the fields of the object For example, you can investigate the properties of the productList variable inside the productDb instance This debugger (like all debuggers in Java) allows you to see the values of private member variables, which are normally inaccessible The syntax for dump (as well as some of the other investigative commands) also accepts an expression You can use any legal Java expression . JSP consists of 91 lines of code. The generated servlet for the exact same page using JSTL consists of 4 19 lines! Of course, one of the benefits of using custom tags is the lack of debugging. example, suppose you have an administration part of your web application that must con- nect to a variety of databases, gather a lot of records, and make web service calls, in addition to other time-consuming. java. io.IOException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import

Ngày đăng: 09/08/2014, 12:22

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

Tài liệu liên quan