Applications need to be deployed to be useful. There's really no point in developing an application that never gets deployed, although this occurs more often than you might think. The need for deployment is obvious, but what about packaging? Does every Struts application have to be packaged before it gets deployed? The short answer is yes. In this chapter, though, we'll examine the long answer.
Before we get into the details of packaging and deploying Struts applications, let's define exactly what these two concepts mean in the context of web applications that are built using the Struts framework. Although the two concepts are closely related, they are not the same thing.
16.1.1 What Is Packaging?
Packaging a Struts application involves gathering all the files and resources that are part of the application and bundling them together in a logical structure. Many different types of resources are usually included in a Struts application, and all of these need to be bundled with the application. Some of the more common types of resources that can be packaged with a Struts application are:
During the design stage, time and effort should be spent on deciding how you are going to structure the application. Not every detail needs to be figured out and resolved before construction begins, but you should understand the target environment's requirements and how these requirements will affect your packaging and deployment strategy. Ideally, you should decide on the principal package and directory structure for the application before construction gets underway. This will help to alleviate the normal miscommunication between developers and reduce the number of redundant resource files.
16.1.2 What Is Deployment?
As the previous section mentioned, packaging and deployment are closely related, but involve different tasks. While packaging determines where the resource files will reside in the package structure and how those resources will be bundled, deployment deals with how the bundled application will be installed and configured inside a target web container.
There are two approaches that you can use when deploying a web application into a container. The first approach is to deploy the web application in a web archive (WAR) file. Most web containers install a WAR file and make it available for users, often without even requiring a restart of the container. This approach is convenient because once the WAR file is properly built, the rest of the work is handled by the container. One of the downsides of this approach, however, is that if a change is made to any file or resource within the web application, a new WAR file must be created and redeployed into the container. The details of how to deploy your Struts application as a WAR file are discussed later in the chapter.
The second approach to deploying a Struts application puts more work on the developer. It still involves packaging your application as a WAR file, but includes manually unpackaging the WAR file into a directory of the container. The exact location of the directory is container-dependent. In Tomcat and Resin, for example, the default web applications directory is webapps. In the WebLogic application server, you have to unpack the WAR file underneath the applications directory. (All three of these containers also allow you to specify alternate installation directories.)
When expanding the WAR file, you need to create a directory into which to unpack the files. The name of the directory is usually the name of the web application. So for example, if the web application was called Storefront and you wanted to install it into Tomcat, you could create a directory called storefront under the webapps directory and unpack the WAR file there.
This deployment approach is referred to as an exploded directory format, because the WAR file is exploded back into its original structure within the container. The benefit of this approach over deploying the WAR file itself is that when there are changes, only the changed files need to be redeployed into the container. This is much easier when you're still developing or debugging the application, but you may not want to leave it like this for production.
16.1.3 Deciding How to Package Your Application
Because Struts applications are web applications, most of the questions about how to package them are answered in the Servlet and JavaServer Pages specifications. A web application must follow a very strict set of guidelines and conventions that make the web application portable across other web containers.
Fortunately, packaging your Struts application in the WAR format solves much of the hassle regarding packaging. However, there are many questions that you will have to resolve. For example, you'll have to decide where all the JSP pages go and whether you should store your Struts Action and ActionForm classes together or in separate action and form packages. Although there are definitely best practices, there are no hard-and-fast rules for resolving issues such as these. Some of the solutions will depend on your organization's requirements and policies.
16.1.4 Namespace Management
A namespace is simply a set of names that may or may not be associated. A namespace is normally used to prevent conflicts or collisions between similar entities and to allow clients to reference these entities by some logical name.
In software development, a namespace is a way to package classes, interfaces, and other types of information into a hierarchical structure. There are many examples of namespaces throughout the computer industry. The manner in which the Internet Domain Name System (DNS) works is a type of namespace. Within the oreilly.com domain, for instance, other IP addresses are linked together in a hierarchal fashion. All of this referencing helps prevent IP addresses from colliding. Another example, which is more closely related to software development, is the namespace that is used within JNDI. But by far the most familiar use of a namespace in Java development is for creating classes and interfaces that reside in a package.
As you know, Java applications are organized as sets of packages. Even when you don't specify a package explicitly, it's still part of a package. The purpose of the package is to prevent name collisions and to help identify entities (in this case, Java classes) easily. When extending the Struts framework with your own classes and interfaces, you need to decide how best to package these components for your application. Not every application will contain the same classes and interfaces.
16.1.5 JSP File Placement
For many developers, where you place the JSP pages may seem like an easy question to answer. Although you may have to decide which directory a particular JSP page belongs in, that's normally all that has to be decided. However, there are situations where more control may need to be placed on who and what has access to the JSP pages. One suggestion is to put the JSPs in a directory underneath the WEB-INF directory.
The purpose of this approach is threefold:
This alternate approach has gained some popularity, although not all of the containers currently support it. Although the 2.3 Servlet specification seems to indicate that something to this effect may be possible, different vendors may not agree in their interpretation of the specification for example, in the past, the WebLogic developers have stated that they interpret section SRV.6.5 of the Servlet specification differently. WebLogic will return a 404 or 500 error code when you attempt to access a JSP page underneath the WEB-INF directory (although there has been some indication that WebLogic will make this option available in future versions of their container).
Even though some containers do support the above approach, you may not need to put the JSP pages underneath the WEB-INF directory. If you call Struts actions only from your web application and don't link to JSP pages directly (which is a requirement for Struts 1.1), this approach will not have much benefit for your applications. There are portable alternatives for example, you can use the security-constraint element in the web.xml file. Just create the required directories for the JSP pages that you want to protect. In the Storefront example, suppose that users shouldn't be able to access the JSP pages underneath the order directory. A security-constraint element like this could then be added:
<security-constraint> <web-resource-collection> <web-resource-name>SecureOrderJSP</web-resource-name> <description>Protect the Order JSP Pages </description> <url-pattern>/order/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name></role-name> </auth-constraint> </security-constraint>
Figure 16-1 shows what happens when a client attempts to access a JSP page directly within the order directory.
Figure 16-1. A 500 error will occur when accessing a protected JSP
When the security-constraint element is added to the web.xml file for the Storefront application, it says that no user without the proper authentication can access the order JSP pages. However, in this example, the role-name element was intentionally left blank and therefore will never match an actual role. You could also have specified that only users with an admin role could access the pages, and then never given that role to an actual user. The container would still be able to access these pages through forwards and includes.
You must be careful what you put in the url-pattern element, however. If you have resources such as images in subdirectories, they will not be available when the browser attempts to download them. Using the /order/* pattern means that nothing under the order directory can be requested directly by the client; this includes images in subdirectories, and the browser will have difficulty receiving the images for a returned HTTP response message.
16.1.6 Precompiling JavaServer Pages
If you are using JavaServer Pages as your Struts presentation technology, you are probably very familiar with the compilation process that the JSP container performs on the application's behalf. When the web container receives a request for a resource with an extension of .jsp, the JSP container is selected to process the request. If this is the first request for the JSP page or if the timestamp for the JSP page is newer than the existing servlet class, the page will normally be compiled.
The JSP container uses a two-step process. First, the JSP page is translated from the JSP code into a Java source file. Each container may have its own implementation to translate the JSP page. The second step takes the Java source file and compiles it into a servlet class file using whichever Java compiler is installed for the JSP container.
Although just-in-time compilation is a nice feature while you are developing or debugging an application, you may not want to make the JSP source available in a deployment package. This is an issue of both security and licensing although the JSP pages should contain only presentation logic, that's still intellectual property. If this is the case, you can precompile the JSP pages and not provide the JSP source code to customers. You'll have to distribute only the compiled bytecode, making it harder for the source code to be seen. Once the JSP pages are compiled into Java class files, you can even run them through an obfuscator to make it harder for them to be viewed through decompilation tools. Precompiling the JSP pages gives you more options for dealing with issues like these.
As always, though, there are some downsides to precompiling JSP pages. For one thing, you lose the ability to update or fix a problem quickly. Instead of just placing a new JSP file onto the server and letting the JSP container compile it when it's accessed, you must do the recompilation by hand and deploy the servlet class. Another downside is that when some containers detect a problem with a single JSP, they will stop processing the rest of the application's JSP pages. When this occurs, you must make sure that every JSP page compiles successfully before the container will deploy the web application. Developers may actually consider this a benefit rather than a disadvantage, but it can be problematic in production environments if you are trying to make a quick fix to a bug.
Unfortunately, there's no standard way to precompile the JSP pages. Each container has a different way of doing it. This section briefly discusses how to precompile pages using three of the available containers on the market: Tomcat, Resin, and WebLogic.
126.96.36.199 Precompiling JSP pages with Tomcat
The JSP implementation in Tomcat, which is called Jasper (or Jasper 2 in Version 5.0), provides a reference implementation for the latest specification. It's packaged along with Catalina, the reference implementation of the latest Servlet specification, inside of Tomcat. The JSP-to-Java compiler is available as a separate program that can be executed from the command line. Its job is to convert a JSP page into a Java source file. From there, a standard Java compiler can convert the source code into bytecode.
The program is called jspc.bat (or jspc.sh, depending on which platform you're using) and is included in the Tomcat installation directory. Many options can be set to configure everything from the output directory to the package name of the Java source. There's even an option that will cause the compiler to create XML that can be added to the web.xml file for each JSP page that gets precompiled. You can do one page at a time or specify an entire web application.
However, as with many things in Java today, Ant is the preferred way of compiling a web application, including JSPs. The Tomcat documentation includes a sample Ant script that will use the jspc tool to precompile the JSPs. Check the Tomcat documentation for your particular version of Tomcat. For Tomcat Version 5.0, see the following link http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jasper-howto.html.
188.8.131.52 Precompiling JSP pages with Resin
To precompile JSP pages using Resin, you can use the httpd.exe command from the Resin bin directory, like this:
resin/bin/httpd -conf conf/resin.conf -compile <URL>
Here, the URL is a JSP resource within a web application installed in Resin.
You can also use the command-line version, like this:
resin/bin/httpd -e <URL>
With this approach, you can compile only a single JSP page at a time, although you could easily create a script that went through your entire web application. An easier way to configure this is to use Ant, as discussed later in this chapter.
184.108.40.206 Precompiling JSP pages with WebLogic
With WebLogic, there are two approaches to precompiling JSPs. The first approach is to manually execute the compiler from the command line. This approach is useful when debugging certain JSP problems.
To run the WebLogic JSP compiler from the command line, you must execute the following command:
java weblogic.jspc -options fileName
Just replace fileName with the JSP you need to compile. There are many options that can be added with the -options flag. For Version 7.0, you can view the list of options at http://e-docs.bea.com/wls/docs70/jsp/reference.html. For Version 8.0, http://e-docs.bea.com/wls/docs81/jsp/reference.html has the available options.
The second approach to precompiling JSPs with WebLogic is to allow the container to precompile the JSPs when an application is deployed or first started. To accomplish this, you need to add a <jsp-descriptor> element.
Using the <jsp-descriptor> element within the weblogic.xml deployment descriptor, you can set the precompile parameter to true.
<jsp-descriptor> <jsp-param> <param-name> precompile </param-name> <param-value> true </param-value> </jsp-param> </jsp-descriptor>
When set, WebLogic will compile all of the JSPs when an application is deployed or redeployed.
However, WebLogic will not deploy the web application if any one of the JSP pages fails to compile. Once it detects an error compiling a page, all compilation stops and the web application will not be deployed until you fix whatever is causing the problem. After fixing it, you must restart WebLogic to start the process all over again.
One of the major drawbacks to using this option is that the server will precompile the JSPs every time it's started or the application is deployed. This can delay how fast the server is ready to accept incoming requests for the application. To avoid this problem, you can precompile the JSPs into .class files using the WebLogic JSP compiler and include them in the WEB-INF/classes directory of the WAR. You will also need to add the following <jsp-descriptor> elements in the WebLogic deployment descriptor.
<jsp-descriptor> <jsp-param> <param-name>precompile</param-name> <param-value>false</param-value> </jsp-param> <jsp-param> <param-name>pageCheckSeconds</param-name> <param-value>-1</param-value> </jsp-param> </jsp-descriptor>
16.1.7 Packaging EJB Resources with Struts
If you're communicating with EJBs in the middle tier, it might be necessary to package some of the EJB resources along with your web application package. Because the web tier acts as a client to the EJB server, certain resources are required to connect and communicate with the beans.
The beans' home and remote interfaces, for example, need to be packaged either in the classes directory or as a JAR file in the lib directory of the web application. Also, some of the JNDI classes need to be included so that clients can acquire home and remote objects. The actual client-side EJB stub classes are not required, however. This wasn't always the case, but the latest specification now describes a mechanism that allows these to be automatically downloaded when a request is made using an EJB remote interface.
In many cases, it's enough to put the EJB container JAR file in the WEB-INF/lib directory. For example, if you are using WebLogic, you can put the weblogic. jar file in the web tier, as it contains all of the necessary client-side resources. You will also need to include any Data Transfer Objects being referenced by the two tiers. They will need to be included on both sides.