< Day Day Up > |
10.2 Replacing the ControllerFirst, let's replace the SearchProductsController. Here's the main method of that class: public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { if (request.getParameter("search") != null) { String keyword = request.getParameter("keyword"); if (keyword == null || keyword.length( ) == 0) { return new ModelAndView("Error", "message", "Please enter a keyword to search for, then press the search button."); } else { PagedListHolder productList = new PagedListHolder( this.petStore.searchProductList(keyword.toLowerCase( ))); productList.setPageSize(4); request.getSession( ).setAttribute( "SearchProductsController_productList", productList); return new ModelAndView("SearchProducts", "productList", productList); } } else { String page = request.getParameter("page"); PagedListHolder productList = (PagedListHolder) request.getSession( ).getAttribute("SearchProductsController_productList"); if ("next".equals(page)) { productList.nextPage( ); } else if ("previous".equals(page)) { productList.previousPage( ); } return new ModelAndView("SearchProducts", "productList", productList); } } The method returns a new instance of ModelAndView and Spring uses it to determine which JSP to load and how to wire data up to it. The method takes an HttpServletRequest and HttpServletResponse in order to interact directly with the HTTP messages. The first thing the method does is make sure the user entered a search term. If not, it displays an error to the user; if so, it creates a PagedListHolder called productList with a maximum page size (number of rows per page) set to four. Finally, it calls the petStore instance's searchProductList method, which calls to ProductsDao and finally returns the HashMap of Product instances. The second clause is for when the user clicks the Next Page or Previous Page buttons on the paged list. 10.2.1 Rewrite or Replace?The next question a conscientious programmer should ask is, does it make more sense to rewrite this class to make use of the Spider, or to write an entirely new controller? In order to answer that question, we need to consider three more-specific questions first:
Here's the answer to these questions: yes, we have the source code; yes, we'll want access to retain the potential for using the old service; and yes, the controller implements a very simple interface. The controller only needs to implement a single method, handleRequest, which takes an HttpServletRequest and a HttpServletResponse and returns a ModelAndView. This means the jPetStore application doesn't need any systemic changes in order to use our new controller, as long as we support that interface. 10.2.2 Implementing the InterfaceTo replace this class, we're going to write our own controller class called SearchPagesController. It must implement the Controller interface, which defines our handleRequest method. public class SearchPagesController implements Controller { ... } Here's our controller's handleRequest method: public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { if (request.getParameter("search") != null) { String keyword = request.getParameter("keyword"); if (keyword == null || keyword.length( ) == 0) { return new ModelAndView("Error", "message", "Please enter a keyword to search for, then press the search button."); } else { ConfigBean cfg = new ConfigBean( ); String indexpath = ""; try { indexpath = cfg.getCurIndexPath( ); } catch(Exception ex) { return new ModelAndView("Error", "message", "Could not find current index path."); } QueryBean qb = new QueryBean(indexpath, keyword, "contents"); qb.execute( ); HashMap hits = new HashMap(qb.getResults( ).length); for(int i =0;i<qb.getResults( ).length;i++) { hits.put("hits", qb.getResults( )[i]); } return new ModelAndView("SearchProducts", hits); } } } For our search functionality, we won't use the paged results. We simply list all the results on a single page; as a result, we don't have to deal with the Next Page and Previous Page code. Our controller again checks for null keywords and returns an error if it finds them empty. Otherwise, the service is used almost identically as the console application in the Chapter 9 was used. First, create an instead of ConfigBean to find the most current index of the site, then create a QueryBean based on that index path. Finally, execute the query and put all the HitBean instances into a HashMap to return to the View. The usage pattern is identical to that in the last chapter; the only difference is the format of our returned data. Instead of passing the native array of HitBeans back, the ModelAndView object requires a HashMap. It's easy enough to create the one from the other, and now we have an entirely new access point for the Spider application with almost no work. There is one last detail we need to work out. The original SearchProductsController has a field called petStore of type PetStoreFacade that the Spring framework populates for it. In order to be a complete replacement for the original, our new controller needs to expose the same property and accessor methods, even though they aren't officially found on a standalone interface anywhere in the application. You will often find examples of this when you're extending or modifying an application. private PetStoreFacade petStore; public void setPetStore(PetStoreFacade petStore) { this.petStore = petStore; } 10.2.3 Registering Our New Class with jPetStoreFinally, we alert jPetStore to the new controller's existence. If jPetStore is not coded for extensibility, we have to modify the application code in order to get it to work. For instance, if there are methods of jPetStore that create instances of SearchProductsController directly, we must change each of those lines to create a SearchPagesController instead. It turns out, however, that jPetStore is quite ready for extensibility—partly because it is based on the Spring framework. In order to tell jPetStore about our new controller, we modify a single configuration file (petstore-servlets.xml). This file tells Spring what objects to create and how to wire them together to make a sensible application. Now, we just need to find the configuration setting used to launch the SearchProductsController and point it to our new SearchPagesController instead. <bean name="/shop/searchProducts.do" class="org.springframework.samples.jpetstore.web.spring.SearchPagesController"> <property name="petStore"><ref bean="petStore"/></property> </bean> We're telling the application to map requests for "/shop/searchProducts.do" to a new instance of SearchPagesController. At the same time, we tell it, provide the SearchPagesController with the current instance of petStore (in a property called petStore). 10.2.3.1 Principles in action
|
< Day Day Up > |