16.2 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 completely specified guidelines and conventions that make the web application portable across other compatible 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.2.1 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.[1] 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.

[1] Any Java class that doesn't have a package statement declared in the source file is considered to be part of the default package.

16.2.2 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:

·         It forces all requests to go through the ActionServlet class.

·         It prevents users from bookmarking a page.

·         It protects the WEB-INF directory and helps protect JSP pages from being called directly.

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

figs/jstr_1601.gif

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.2.3 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.[2]

[2] Most containers have an option that disables detection so that pages that have changed will not be recompiled. This option is usually selected in a production environment because incremental changes should not be introduced in production. Also, allowing this to occur would amount to a security risk.

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 4.0, Resin 2.0, and WebLogic 7.0.

16.2.3.1 Precompiling JSP pages with Tomcat

The JSP implementation in Tomcat, which is called Jasper, 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 located in the bin directory of 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.

16.2.3.2 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.

16.2.3.3 Precompiling JSP pages with WebLogic

With WebLogic 6.0, you have to include a context-param element in the deployment descriptor for each web application. This will instruct WebLogic to compile every JSP page that is part of the web application when the application server starts up. The following context-param element must be added to the web.xml file:

<context-param>
  <param-name>weblogic.jsp.precompile</param-name>
  <param-value>true</param-value>
</context-param>

WebLogic 6.0 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. You have to fix whatever is causing the problem and then restart WebLogic to start the process all over again.

16.2.4 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 6.0 or higher, you can put the weblogic.jar file in the web tier, as it contains all of the necessary client-side resources.