Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 37 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
37
Dung lượng
831,65 KB
Nội dung
205Managing Tomcat Lite from the build In addition to the convenience that running the shell from a standard build provides, you also have all of the power of Maven at your disposal. The mvn clean install com- mand will compile and bundle your project into a WAR artifact that can be deployed to any JEE servlet container. You can also package your project into a module by sim- ply issuing the command mvn clean jar:jar (presuming you have included the source in the POM as discussed). You can run tests, including those based on GwtTest- Case (which we’ll cover in detail in the next chapter) by using mvn clean test . The behavior of most of the GWT-Maven goals should be fairly obvious. The bene- fit of Maven, and these goals, is that once you have a configuration in place, you can use, change, and share your build easily and get consistent results. Another reason for using the GWT-Maven plugin, beyond the general convenience for GWT tasks it pro- vides, is the way it helps to manage and merge the WAR file deployment descriptor, web.xml. This file is used in hosted mode with the embedded Tomcat server GWT includes, and in web mode when you deploy your application as a WAR file. Because of this dual use, there are a few configuration overlap points and divergences that have to be dealt with. 7.4 Managing Tomcat Lite from the build We first discussed Tomcat Lite, the embedded Apache Tomcat instance included in GWT, in chapter 3. There we discussed the issues surrounding the maintenance of multiple configuration files—one for the hosted mode and one for the web mode—and how to manage those configuration files so that your projects can make use of service-side features provided by the JEE container. Tomcat Lite maintains its own set of context.xml ( ROOT.xml in GWT terms) and web.xml configuration files. Obviously, the ideal deployment process would need to work both in hosted mode terms and in terms of creating a WAR file. This means what we really need is a context.xml file and a web.xml file, which are local source files for our project, and which get copied into and deployed with our WAR files and also are used in hosted mode. Creating an automated build process that allows the inclusion of project-local configuration files (context.xml and web.xml) that are used both for creating WAR files and for automatically configuring Tomcat Lite has several advantages. First, you can maintain configuration information in one place, rather than in several. Sec- ond, you can use common JEE container-managed resources, such as servlet filters, security realms, and data sources in hosted mode. Third, if you import any module that includes servlet declarations, they are automatically available to your module with- out your having to just know about them and manually configure them. (This third point, and the fact that GWT testing, something we will discuss in the next chapter, needs the embedded Tomcat, are the main reasons not to abandon the embedded instance altogether.) TIP You may recall from chapter 3 that you do not have to use the embedded Tomcat instance supplied with GWT. Instead, you can use the -noserver 206 CHAPTER 7 Building, Packaging, and Deploying option and configure an external JEE container instance on your own. If you use -noserver , and you plan to share your module with others, you need to include instructions for any server-side resources required. To automate the process of handling this configuration, you have to be aware of the various files involved, and you need to have a process in place that automatically con- figures resources based on the context—whether hosted mode or WAR. PROBLEM We need server-side resources, GWT service servlets, and other resources to automati- cally be available both for hosted mode use and when deployed as a WAR file. SOLUTION It’s standard practice to include JEE configuration files, such as the web.xml deploy- ment descriptor, as source resources in your project. Then, at build time, these resources are normally copied into the proper distributable locations, such as WEB- INF . This is exactly what we did in section 7.2.4 when we used an Ant build to create a WAR file. Using this approach, the web.xml file can be specific to the project, man- aged by source control, and modified with each build without affecting other builds. The key is that the configuration file is part of the source, and is copied into the distri- bution at build time. You can extend this concept to manipulate the Tomcat Lite configuration files based on the same project-local configuration data. That is to say, you can use the manipulation and copying of files that a build process provides not only to create a distribution artifact (such as copying web.xml to where it belongs for a WAR file), but also to configure the embedded GWT Tomcat instance. DISCUSSION In order to manage the Tomcat configuration for both the GWT-embedded Tomcat instance and for a deployable WAR file, we’ll need to manipulate and merge several source files. We’ll use the deployment descriptor web.xml file, the Tomcat-specific configuration context.xml file, and the GWT module file. Listing 7.5 shows a sample MyProject web.xml file—this is meant to represent the web.xml local to your application. This file contains no references to GWT service serv- lets (because we’re going to inject those based on information from the module), but it may certainly contain other resources such as servlet filters, non- GWT servlets, data- base references, and so on. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>MyProject</display-name> <filter> <filter-name>MyFilter</filter-name> <description>This is a filter that does something Listing 7.5 A sample web.xml project deployment descriptor Include servlet filter 207Managing Tomcat Lite from the build interesting, like log requests</description> <filterclass> com.manning.gwtip.myproject.server.MyServletFilter </filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <distributable /> <session-config> <session-timeout>30</session-timeout> </session-config> <welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app> Listing 7.5 shows a fairly standard web.xml file, and that’s good. That’s what we want in terms of the source file. Next, listing 7.6 shows the embedded GWT Tomcat included ROOT/WEB-INF/web.xml file. <?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>shell</servlet-name> <servlet-class> com.google.gwt.dev.shell.GWTShellServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>shell</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> The only notable thing about the standard Tomcat Lite web.xml file, shown in listing 7.6, is that it has a servlet entry for GWTShellServlet b . In order to use something like our own filter from listing 7.5 within hosted mode, we basically need a combination of these files, as is shown in listing 7.7. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc. //DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Sample Server</display-name> <filter> Listing 7.6 The base GWT web.xml in tomcat/webapps/ROOT/WEB-INF Listing 7.7 The merged GWT hosted mode Include GWTShellServlet b 208 CHAPTER 7 Building, Packaging, and Deploying <filter-name>MyFilter</filter-name> <description>This is a filter that does something interesting, like log requests</description> <filter-class>gwtip.module.sample.server.MyServletFilter</filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <! inserted by GWT-Maven > <servlet> <servlet-name>shell</servlet-name> <servlet-class> com.google.gwt.dev.shell.GWTShellServlet </servlet-class> </servlet> <! inserted by GWT-Maven > <servlet-mapping> <servlet-name>shell</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> The combination of the project-specific web.xml file and the default GWT hosted mode web.xml file, as seen in listing 7.7, gives us a new file for use in hosted mode that enables our ServletFilter to be present b and the GWTShellServlet also to remain c . This will ensure that our filter is there when our application runs in the shell. This combination provides our hosted mode support and enables us to maintain our own configuration in that environment. But, that’s the only environment where this file will work; it won’t work in web mode. So, we have one more web.xml resource we need to deal with. We need a deployment-specific web.xml file for our WAR file that includes all of our GWT service servlet entries. Listing 7.8 displays this final web.xml incarnation—the one that needs to go in our WAR file for deployment. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Sample Server</display-name> <filter> <filter-name>MyFilter</filter-name> <description>This is a filter that does something interesting, like log requests</description> <filter-class> gwtip.module.sample.server.MyServletFilter </filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> Listing 7.8 A deployable web.xml file Include ServletFilter b Include GWTShellServlet c Include ServletFilter again b 209Managing Tomcat Lite from the build <url-pattern>/*</url-pattern> </filter-mapping> <! inserted by GWT-Maven > <servlet> <servlet-name> gwtip.module.sample.server.MyServiceServlet/MyService </servlet-name> <servlet-class> gwtip.module.sample.server.MyServiceServlet </servlet-class> </servlet> <! inserted by GWT-Maven > <servlet-mapping> <servlet-name> gwtip.module.sample.server.MyServiceServlet/MyService </servlet-name> <url-pattern> /gwtip.module.sample.server.MyServiceServlet/MyService </url-pattern> </servlet-mapping> </web-app> In our final web.xml the ServletFilter element from our original source project- specific file is still present b , and we additionally have an explicit <servlet> element referring to a GWT service servlet c . Remember, we cannot use the GWTShellServlet in a deployed WAR file; rather, we must have these explicit servlet entries. We can get the servlet entries for the deployment-time web.xml file by inspecting our GWT mod- ule file (and all of those in the <inherits> chain) and including <servlet> and <servlet-mapping> entries for each servlet defined there. At this point, we’re dealing with four web.xml files, which is admittedly not an ideal situation. Additionally, if we wanted to configure something outside of the stan- dard web.xml, such as a context-managed DataSource reference for use in hosted mode, we would have to include our own application-local context.xml file as well. Then we would need to apply that file to Tomcat Lite, too. This source file would be placed in our application at src/main/webapp/ META-INF/context.xml (in similar fashion to an src/main/webapp/ WEB-INF/web.xml file). Then, before the embedded Tomcat instance is invoked, we would need to replace the GWT Tomcat Lite file, tom- cat/conf/gwt/localhost/ ROOT.xml, with our own (renamed to ROOT.xml). You can manage this process manually by maintaining multiple web.xml files (one for hosted mode, one for deployment) and a context.xml file, and then overwriting the Tomcat Lite configuration as needed, but this is not very convenient. Or you can elect to go the -noserver path and not even try to configure the Tomcat Lite instance. This approach is very powerful and helpful, but it really just shifts the configuration problem rather than solving it. What we have chosen to do is to automate the manage- ment of this process through the GWT-Maven plugin. This is what the mergewebxml goal mentioned in table 7.3 does. Using GWT-Maven, you maintain only two files. These are the same two files you maintain for any standard JEE servlet-based application using Tomcat, a project-local Notice that <servlet> element is generated c 210 CHAPTER 7 Building, Packaging, and Deploying web.xml file and a project-local context.xml file. The plugin does the merge for you. The GWT-Maven plugin can configure Tomcat Lite for hosted mode, can manage the required hosted mode classpath, and will also configure the correct web.xml for deployment. If you do not wish to use Maven, you could use the same approach with other build tools as well. The main point is that source files can be used to both con- figure the embedded GWT Tomcat instance and create what is needed for a deploy- able WAR file—and all of this can be automated. This is admittedly an involved process, but once it’s automated it’s a huge help. With an automated build that manages the process, you can control the configuration in the source, just as with standard non- GWT Java projects. Also, you can share GWT projects that involve not only RPC but other server-side resources and concepts, such as filters and data sources, without requiring others to manually set up the required configuration for hosted mode. 7.5 Summary Configuring a GWT project for build and deployment can be confusing at first, but things on the inside are really not much different from a combined standard web application and a JEE servlet-based application. The compounding factor is that you basically have two environments to build for: hosted mode and web mode. Even though you aren’t deploying things for hosted mode, you still have to get the configu- ration correct so that the development shell is productive. And when deployment time does arrive, you need the same configuration to work in an external setting. In this chapter we addressed building and packaging GWT modules, and building and deploying GWT-based applications. Along the way, we also brought the Ant and Maven automated build tools into the fold. These tools can be used to manage config- uration elements and direct the build process, even down to the configuration of the GWT-embedded Tomcat instance. In the next chapter, we’ll extend the concepts behind the automated build to include testing and benchmarking. Then we’ll bring all of those components together, again with Maven, to build and test a GWT project using a continuous integration server. 211 Testing and Continuous Integration A test that reveals a bug has succeeded, not failed. —Boris Beizer Completing your code and turning out a quality application are not often synony- mous nor simultaneous. Beyond the syntax, the compiler, and the resulting bits, a quality application is usually the result of careful design, adequate testing, and proper component integration. These fundamental concepts can be applied to all software projects, but putting them to work with GWT is a bit different than in other environments. In this chapter, we’re going to follow up on some of the tasks related to build- ing, packaging, and deploying that we addressed in the last chapter, with a focus This chapter covers ■ Understanding and implementing GWT tests ■ Exploring advanced testing techniques, remote and benchmark ■ Obtaining code coverage information with GWT tests ■ Using continuous integration with GWT projects 212 CHAPTER 8 Testing and Continuous Integration on testing and automating builds. Along with testing, we’ll also touch on benchmark- ing in a GWT context. Once we have all the pieces in place, we’ll work through a sam- ple project that utilizes Maven and a continuous integration ( CI) server, in order to take full advantage of the complete process. All of these concepts go hand in hand because testing and metrics should ideally be part of an automated build process that’s CI managed. The concepts of testing, benchmarking, and continuous integration are fairly well accepted tenets in any good software development practice, regardless of language or technology. Even though we cannot hope to cover these broad subjects comprehen- sively in a single chapter, we’ll go into some detail on the general principles, and we’ll focus on getting these things working with a GWT project. 8.1 GWT testing To make testing possible, GWT provides several testing tools and related supporting classes, which allow you to run tests against Java bytecode in hosted mode or against JavaScript in web mode. These are the same concepts that apply during development when using the GWT shell. When using web mode testing, a special JavaScript translat- able JUnit support layer is automatically invoked by the toolkit. Before we begin looking at GWT testing, we first need to narrow down what the testing support is intended to address and what it is not. We’ll also look at some of the common issues concerning testing and touch on the testing process. Once we have that background, we’ll work through several GWTTestCase -derived tests to hash out the details. After we have looked at testing GWT client-side code, we’ll also briefly cover testing server-side code, and entire applications, outside of the toolkit. Knowing the what, how, and where of testing will enable you to create better tests, which in turn will help you better understand your code and the toolkit, which will result in better applications. 8.1.1 Knowing what to test The testing support provided by GWT is intended for testing your GWT client-side code, but not your UI. This may sound contradictory, and possibly even controversial, but it’s important to understand it nonetheless. Non- UI client-side code includes your client-side model (logic and data) and any client-side controller you may be using, but not the view. In GWT unit testing, you should test asynchronous application-related event handling, client model beans, client-side logic, and the client side of GWT RPC. You should not worry about testing browser-based events (button clicks, mouse move- ments, and the like) or panel layout. You can, with some special test and event-related helper code, test UI concepts within GWT unit tests, but you should not be very concerned about doing so. When you click on a GWT Button , the ClickListener that responds is part of the toolkit itself, not your code. If that ClickListener contains non-UI logic, in the onClick() method for example, you might be tempted to try to test the button in order to exercise 213GWT testing your code. Yet, an often cleaner and easier practice is to move the logic from the UI widget into a client-side model, and invoke it through a client-side controller. (We dis- cussed these concepts in chapters 2 and 4, and have used them in the examples throughout the book; we’ll do the same with the code later in this chapter.) In other words, design your application so that you can test logic and events through a controller method, rather than through the GWT UI. With such a design, you can test the controller directly without needing to worry about a Button . There are many benefits to the separation of responsibilities MVC provides, and making unit testing easier and more focused, especially in the context of a UI toolkit, is a signifi- cant one. We also need to stress that we’re referring to GWT unit tests in this chapter. You should still test the overall UI in integration or acceptance testing with tools outside of GWT. Also, when testing non-GWT specific code, such as server-side code, you do not need GWT support. In those cases, you should use standard JUnit or similar testing mechanisms. We’ll come back to this in section 8.1.5 when we cover testing from out- side the GWT perspective. GWT tests have a specific scope, translatable client-side logic and data, and RPC marshaling and interaction. Other testing mechanisms can pick up where these tests leave off. Deciding what to test using the GWT testing support involves being familiar with all the moving parts. Knowing a bit about what is happening behind the scenes will help you utilize and troubleshoot GWT tests. 8.1.2 How GWT testing works GWTTestCase is where it all begins. When you write a test case in GWT, you extend GWT- TestCase , which itself extends the standard JUnit TestCase . GWTTestCase requires you to tell the test what module it will be using via the getModuleName() method and includes several other very simple methods that allow the adding and removing of checkpoints (these are used to simulate something resembling a stack trace in web mode, which otherwise would not be possible). GWTTestCase also invokes the JUnit shell, the all-around traffic cop of the GWT testing system. The JUnit shell is an extension of the standard GWT shell, with test-specific capabil- ities. It is basically a harness that controls all the GWT magic for tests. One special aspect of the JUnit shell is that it’s invoked automatically for you, so you can’t pass arguments directly to it the way you’d do with the standard GWT shell. The JUnit shell supports many of the same options as the GWT shell, but you need to use the -Dgwt.args system property when invoking a test runner to route arguments to it. We’ll see how to use this technique in several examples later in this chapter. The JUnit shell also forces the inheritance of a special GWT JUnit module into each test module that passes through it, and it caches module definitions. The GWT JUnit module does several important things. First, when the module def- inition loader encounters classes that are of GWTTestCase type, it defines the genera- tor used to create JUnit test case stubs, as follows: 214 CHAPTER 8 Testing and Continuous Integration <generate-with class="com.google.gwt.junit.rebind.JUnitTestCaseStubGenerator"> <when-type-assignable class="com.google.gwt.junit.client.GWTTestCase"/> </generate-with> Generators, as we learned in chapter 1, are used in GWT in conjunction with deferred binding to create source files for later use in the compilation process on the fly. Each stub test class uses a few conventions to provide information about the test to the JUnit system. The second thing the JUnit module does is to set the servlet path in the module definition to the special JUnitHost class. This class acts as the server-side endpoint for GWT JUnit tests. All the moving parts— GWTTestCase , JUnitShell , the GWT JUnit module, and JUnitHost —operate in a loop with a message queue, as outlined in figure 8.1. For this reason, tests are not inline, and TimeoutException s can sometimes occur. In these cases, the testing harness may not always know exactly why your test didn’t complete. We’ll elaborate more on this in the next section, when we cover common testing issues. Although there are several components involved in GWT testing, and having an overall understanding of the process helps, don’t worry if it seems daunting at first. You don’t have to dig into the details in most cases, because the toolkit handles the complexity and translation for you. With a few guidelines in mind, you can simply con- centrate on writing GWTTestCase -derived tests. However, it is helpful to be aware of some common pitfalls. 8.1.3 Testing gotchas GWT testing throws a few curves at the traditional Java developer. These differences from more familiar testing practices are not difficult or cumbersome to deal with once Test environment Client classes JUnit TestCase YourGwtTest (extends GWTTestCase) JUnit module JUnitShell GWTTestCase (hosted mode version) GWTTestCase (web mode version) Translatable JUnit support classes Server classes JUnitHostImpl JUnitMessageQueue Figure 8.1 The high-level components in the GWT testing process, including the JUnit shell and module [...]... com.manning.gwtip.testme.TestMe/?AC280ACA 277 9\ 90CD7CF16A64E5A116D5?com.manning.gwtip.testme.client.MyService?addOrUpdat\ 226 CHAPTER 8 Testing and Continuous Integration ePerson?com.manning.gwtip.testme.client.model.Person?com.manning.gwtip.te\ stme.client.model.Person/29951 978 03?Bob?Marley?1?2?3?4?1?5?6 ?7? 8? serializedRequest - 3?0?8?http://localhost:8888/ com.manning.gwtip.testme.TestMe/?AC280ACA 277 9\... we’re using GWTMaven as a plugin c, we can include the runTarget and compileTarget, which we introduced in chapter 7, and we can include the special testGwt goal d When Maven plugins define goals, they also define which phase of the build they are intended for—the goal knows when to run if it’s included in the In addition to the POM file, we also need to include some machine-wide settings... outline in place and enable you to create IDE- or Ant-related files respectively Along those same lines, GWT includes JUnitCreator for wiring up some initial GWT testing PROBLEM We want to get a basic GWTTestCase test in place and running quickly, so we can familiarize ourselves with the concepts involved 218 CHAPTER 8 Testing and Continuous Integration SOLUTION The most basic test scenario in GWT. .. complete testing regimen Since we have already addressed internal GWT testing in the previous sections of this chapter, we’re going to look at standard Java testing of the server side of a GWT application here, and provide a few pointers about broader acceptance testing PROBLEM We want to test the server side of a GWT application when GWT RPC is involved SOLUTION In keeping with standard programming practices,... delayTestFinish() and finishTest(), enable testing of asynchronous GWT RPC 222 CHAPTER 8 Testing and Continuous Integration To demonstrate using the GWT s asynchronous testing support, we’ll use a controller object to send a Person bean across the wire The test shown in listing 8.3 performs this function Listing 8.3 GWT asynchronous testing support public class GwtTestMyService extends GWTTestCase... as what is shown in listing 8.5 Listing 8.5 Capturing the RPC call structure serializedRequest - 3?0?8?http://localhost:8888/ com.manning.gwtip.testme.TestMe/?AC280ACA 277 9\ 90CD7CF16A64E5A116D5?com.manning.gwtip.testme.client.MyService?addOrUpdat\ ePerson?com.manning.gwtip.testme.client.model.Person?com.manning.gwtip.te\ stme.client.model.Person/29951 978 03?Johnny?Clegg?1?2?3?4?1?5?6 ?7? 8? serializedRequest... newPeople.size()); finishTest(); } }); delayTestFinish(TEST_DELAY_2); { 223 GWT testing controller.addOrUpdatePersonSlow(person); } } h Invoke slow test method In the test shown in listing 8.3, we’re exercising GWT RPC and a client-side model and controller This allows us to go through several important points in a GWTTestCasebased test DISCUSSION In listing 8.3, we again extend GWTTestCase and use... general, including GWT Next we’ll look at a collection of advanced testing topics relating to GWT: remote GWT unit testing, patching GWT (or using an extension JAR) to enable code-coverage data to be collected when running unit tests, and using the GWT Benchmark test extension 8.2 Advanced testing concepts One useful utility GWT provides is the Benchmark class Benchmarks allow you to extend your tests to include... data, testing in web mode, and testing the serialization that GWT RPC entails The testing framework that GWT provides is very valuable, but there are also other ways to test some portions of GWT applications 224 8.1.5 CHAPTER 8 Testing and Continuous Integration Testing outside of GWT Testing outside of GWT is often desirable because it’s simpler and easier than testing inside of the toolkit Testing components... GWT- Maven plugin in conjunction with a coverage-enabled version of GWT Advanced testing concepts 235 DISCUSSION We first met the GWT- Maven plugin in chapter 7 By setting GWT- specific properties for the GWT- Maven plugin, and some additional EMMA properties to generate a report, we can achieve a completely automated build with testing, coverage, and reporting In standard Maven 2 usage, the Surefire plugin (the . <filter> Listing 7. 6 The base GWT web.xml in tomcat/webapps/ROOT/WEB-INF Listing 7. 7 The merged GWT hosted mode Include GWTShellServlet b 208 CHAPTER 7 Building, Packaging, and Deploying <filter-name>MyFilter</filter-name>. information with GWT tests ■ Using continuous integration with GWT projects 212 CHAPTER 8 Testing and Continuous Integration on testing and automating builds. Along with testing, we’ll also touch. general principles, and we’ll focus on getting these things working with a GWT project. 8.1 GWT testing To make testing possible, GWT provides several testing tools and related supporting classes,