C H A P T E R  4

Deploy an Application

This Suntrademark ONE Application Framework Deploy an Application chapter covers the preparation of a Sun ONE Application Framework application for deployment in most J2EE containers, as well as deployment-time configuration of a Sun ONE Application Framework application.

It assumes general familiarity with Sun ONE Application Framework, as well as Web Application Archive (WAR) files, deployment descriptors, and your J2EE container's specific deployment method.



Note - Sun ONE Application Framework applications can be run in any Servlet 2.2/JSP 1.1-compliant (J2EE 1.2) Web container.




Configure the Application

In addition to packaging a Sun ONE Application Framework application, it must be configured before it can be deployed.

If you are using the Sun ONE Application Framework IDE toolset, all of the following configuration is automatically managed for you.

This section is intended to provide information to developers who want to create Sun ONE Application Framework applications or objects by hand, or for developers who want to understand the details of how the framework operates.

Module Servlet Configuration

General Configuration

The Sun ONE Application Framework servlet infrastructure established by ApplicationServletBase includes the ability to configure arbitrary properties on each module servlet using reflection. Parameters are specified in the application deployment descriptor (web.xml) as either context or servlet init parameters using a special name format:

jato:<class name expression>:<param name>

For example:

<context-param>
    <param-name>jato:fooapp.main.MainModuleServlet:foo</param-name>
    <param-value>bar</param-value>
</context-param>

The specified class name might contain an asterisk wild card character to allow parameters to be set on more than one object:

<context-param>
    <param-name>jato:fooapp.*:foo</param-name>
    <param-value>bar</param-value>
</context-param>

Each parameter might only contain a single asterisk, and will match any classes whose class name matches the string before and/or after the asterisk.

The class name expression might also be omitted if the parameter should apply to all module servlets:

<context-param>
    <param-name>jato:enabledLogLevels</param-name>
    <param-value>ALL</param-value>
</context-param>

To have a parameter set on it, the module servlet class must have a setter method that conforms to the JavaBeans method naming convention. For example, a parameter named foo would cause the servlet to call a method called setFoo() to set the parameter value. The value is converted to the appropriate type for the setter method. If the type cannot be converted, an error message is written to the servlet context indicating the exception.

The following table shows the module servlet parameters that are currently available.

Parameter Name

Type

Description

Required

allowShortViewBeanNames

boolean

Versions of Sun ONE Application Framework prior to 2.1 required that ViewBean classes be named with a ViewBean name suffix, for example, FooViewBean. Beginning with version 2.1, ViewBeans do not require this suffix.

The mechanism that looks up ViewBeans from the request's path info must know whether to enforce the ViewBean suffix or not. By default, this parameter is false by default for backward compatibility, but newer application can enable this feature to avoid this requirement.

Note: It is possible to set this parameter on a per-module basis, so parts of the application using the older and newer naming conventions can coexist.

No

enabledLogLevels

String

A comma-delimited list of log levels that should be enabled. Possible values:

NONE

MANDATORY

STD

STANDARD

VERBOSE_DEBUG

TERSE_DEBUG

ANY_DEBUG

JATO_TRACE

JATO_QOS_TRACE

APP_TRACE

ANY_TRACE

WARNING

ERROR

CRITICAL

ANY_ERROR

USER_LEVEL_1

USER_LEVEL_2

USER_LEVEL_3

ANY_USER_LEVEL

ALL

DEFAULT

No

echoLogToSystemOut

boolean

Sets the state of logging to System.out in addition to the servlet context.

No

enforceStrictSessionTimeout

boolean

During development of an application, redeploying an application generally times out any sessions associated with it, which can make iterative development difficult. When set to false, this parameter allows session timeouts to avoid causing the user's browser to be unusable for the current application until closed and reopened.

No

generateUniqueURLs

boolean

Enables or disables generation of unique URLs during page rendering. Unique URLs help defeat problematic caching strategies of proxies and browsers. For example, if the browser is caching dynamic pages inappropriately, enabling this feature will generally cause the browser to avoid showing inappropriately cached dynamic pages by making it think that every page comes from a unique URL. This feature is disabled by default because it incurs a small runtime cost.

No

logMessagePrefix

String

Sets the log message prefix (used to make log messages stand out better).

No

moduleURL

String

The URL for the module servlet. See detailed description below.

Yes

qualifiedViewNameSeparator

char

Since the initial version of Sun ONE Application Framework, the period (".") has been used to separate qualified view names. This separator might make it more difficult for developers to use JavaScript in their HTML pages, however, so instead application developers might set an alternative value here, such as underscore ("_"). Developers must take care not to use the separator character in normal child view names.

No

showMessageBuffer

boolean

Enables or disables showing the application message buffer at the bottom of the rendered HTML page. This feature is useful for debugging, but should be turned off in deployment.

No

useTaglibTEI

boolean

Previous versions of J2EE containers had problems generating Tag Extra Information (TEI) declared in the Sun ONE Application Framework TLD. Beginning with version 2.1, TEI has been enabled by default, but developers with applications running in older container versions might want to disable this feature to maintain compatibility.

No


Module URL Configuration

Each module servlet has an associated module URL. This URL is a standard servlet URL path mapping, and must be configured in the application's deployment descriptor (or equivalent). When each page in a Sun ONE Application Framework application is rendered, the Sun ONE Application Framework tag library renders references to the current module's URL into the HTML output, so that submitted forms or activated links return to the module and ViewBean that rendered the original output. Therefore, the module URL for each module must be configured in a standard way that makes it accessible to the Sun ONE Application Framework infrastructure. Additionally, to allow cross-module navigation within the application, the module URLs of each module servlet must be available to each of the other module servlets. Therefore, the module URL is the only module servlet parameter that must be configured before the application can be run.

The moduleURL parameter is configured like other module servlet parameters, but has some additional restrictions, as follows:

Because the moduleURL parameter is used also by the ViewBeans in each module, each of which share the same package name, the moduleURL parameter must be named as follows:

<param-name>jato:[module package name].*:moduleURL</param-name>

Whereas other module servlet parameters can be configured as either context parameters or servlet init parameters, the moduleURL parameter must be configured as a context parameter (see below for an example). The reason for this restriction is that the moduleURL parameters for each module servlet must be available to all other module servlets.

The URLs rendered into a Sun ONE Application Framework page are relative URLs. Because of the standard Sun ONE Application Framework URL format, which specifies the page name at the end of the URL path, the moduleURL must be configured as follows:

<context-param>
    <param-name>jato:[app package].[module package].*:moduleURL</param-name>
    <param-value>../[module package]</param-value>
</context-param>

Similarly, the module servlet URL must be mapped to the following URL:

<servlet-mapping>
    <servlet-name>[servlet name]</servlet-name>
    <url-pattern>/[module package]/*</url-pattern>
</servlet-mapping>

Technically, the URL path of the module need not be named after the module package--it could be any arbitrary name. Although this is what is recommended for simplicity, the main constraint is that the URL path used in the moduleURL parameter be the same as that used in the servlet mapping URL pattern.

Given the above rules and assuming the following,

a basic application would have the following deployment descriptor:

<web-app>
 
    <context-param>
        <param-name>jato:fooapp.main.*:moduleURL</param-name>
        <param-value>../main</param-value>
    </context-param>
 
    <servlet>
        <servlet-name>MainModuleServlet</servlet-name>
        <servlet-class>fooapp.main.MainModuleServlet</servlet-class>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>MainModuleServlet</servlet-name>
        <url-pattern>/main/*</url-pattern>
    </servlet-mapping>
 
...
 
</web-app>

Adding a second module named Module2 to this application would result in the following:

<web-app>
 
    <context-param>
        <param-name>jato:fooapp.main.*:moduleURL</param-name>
        <param-value>../main</param-value>
    </context-param>
 
    <context-param>
        <param-name>jato:fooapp.module2.*:moduleURL</param-name>
        <param-value>../module2</param-value>
    </context-param>
 
    <servlet>
        <servlet-name>MainModuleServlet</servlet-name>
        <servlet-class>fooapp.main.MainModuleServlet</servlet-class>
    </servlet>
 
    <servlet>
        <servlet-name>Module2Servlet</servlet-name>
        <servlet-class>fooapp.module2.Module2Servlet</servlet-class>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>MainModuleServlet</servlet-name>
        <url-pattern>/main/*</url-pattern>
    </servlet-mapping>
 
    <servlet-mapping>
        <servlet-name>Module2Servlet</servlet-name>
        <url-pattern>/module2/*</url-pattern>
    </servlet-mapping>
 
    ...
 
</web-app>

ViewBean Display URL Configuration

Each ViewBean in a Sun ONE Application Framework application has a peer JSP, and the URL of this JSP is referred to as the display URL of the ViewBean. Each ViewBean has a default display URL it expects to use, and this URL is available/settable via the ViewBean.getDefaultDisplayURL() and ViewBean.setDefaultDisplayURL(String) methods. Developers are normally expected to set the default display URL in a ViewBean's constructor.

However, in some cases, it might be useful or necessary to override the default display URL of a ViewBean at application deployment time. In this case, the deployer can set a context parameter in the deployment descriptor that will set the a ViewBean's default display URL at runtime:

Parameter Name

Type

Description

defaultDisplayURL

String

The display URL for the specified ViewBean.


This parameter is set using the naming convention established above for module servlet parameters (however, it is not a module servlet parameter):

<context-param>
    <param-name>jato:fooapp.module1.FooViewBean:defaultDisplayURL</param-name>
    <param-value>/fooapp/module1/Foo-Alternate.jsp</param-value>
</context-param>



Note - Using this mechanism to override ViewBean display URLs is not generally recommended, as it introduces some overhead. Instead, the affected ViewBeans should be recompiled with the appropriate display URL directly, if possible.



SQLConnectionManager Configuration

The Sun ONE Application Framework contains a manager object called SQLConnectionManager. An instance of this object will be available to an application from the RequestContext on any given request. The purpose of this class is to make the task of obtaining a JDBC Connection object easier for developers and the built-in Sun ONE Application Framework objects.

The basic usage of the SQLConnectionManager is that callers ask it for a JDBC Connection using what is called a datasource name. This datasource name is an arbitrary, logical name for a pre-configured database connection. This class also standardizes this task regardless of the technique used to obtain the connection. Finally, the class provides a level of indirection that can be useful when switching between development, test, and deployment environments.

In standard Java applications, JDBC Connections are normally obtained via a call to the java.sql.DriverManager. Callers provide a JDBC URL specific to the database they want to use, and the DriverManager matches an available JDBC driver with the specified URL, obtains a database connection from the driver, and returns it to the caller. The caller then uses the connection as long as it wants before closing it.

In Web applications, the use of the DriverManager is highly inefficient because it requires a new database connection to be opened and initialized for each application request that requires database access. However, the JDBC 2.0 Standard Extension (the javax.sql package) defines a standard J2EE mechanism for database connection pooling. This allows connections to be reused over multiple requests, and avoids the inefficiency of repeatedly opening connections to the database.

The JDBC 2.0 Standard Extension provides a JNDI mechanism through which Web application developers can obtain a pooled JDBC Connection object. This mechanism consists of allocating a JNDI context and asking it for a datasource by name. This name, also called a datasource name, is of a standard form which begins with "jdbc/". The idea is that instead of embedding JDBC URLs directly in an application, the application deployer pre-configures a JDBC datasource with all the necessary information--host, protocol, username, password, etc.--and makes it available under a logical datasource name that begins with the prefix "jdbc/". Application developers only reference this logical datasource name, which they assume will be mapped appropriately in whatever container the application is deployed.

Unfortunately, not all containers provide this datasource mechanism, and/or they impose certain limitations on the datasource name. For example, one container might allow arbitrary names after the standard "jdbc/" prefix, where another container might require the addition of the application context name after the prefix. This might not be a problem with an application developed and deployed in the same container; however, it's fairly common for an application to be developed and deployed under different containers, making this situation a potential problem.

This is where the SQLConnectionManager seeks to provide assistance. SQLConnectionManager contains its own datasource mapping mechanism, which allows the developer to use truly arbitrary datasource names in their application, yet still allow these names to be operational within any given container. Additionally, the SQLConnectionManager allows mapping of datasource names to either JNDI datasource names or plain JDBC URLs. This feature allows a Sun ONE Application Framework application to run in a container that does not support the JDBC 2.0 Standard Extension, albeit without the benefit of connection pooling.

In your application, you can obtain a JDBC connection from SQLConnectionManager directly using its getConnection() or obtainConnection() methods. The static obtainConnection() method is used to obtain connections outside of the scope of a request, such as during application initialization. You can also bypass SQLConnectionManager and go directly to a JNDI lookup (or to DriverManager) if you want, but the Sun ONE Application Framework classes that use JDBC, such as QueryModelBase, will always use SQLConnectionManager to obtain database connections.

Setting the SQLConnectionManager to use JNDI or the JDBC DriverManager

As mentioned above, the SQLConnectionManager allows either use of plain JDBC URLs (via the JDBC DriverManager) or JNDI datasource names when obtaining a JDBC Connection using an arbitrary datasource name. These two modes are mutually exclusive, and the current mode is selected by calling the SQLConnectionManagerBase.setUsingJNDI(Boolean) static method. This method need only be called once; call this method from the static initializer of your application servlet class.

Calling the setUsingJNDI() method with a value of true will enable JNDI lookups of datasource names. In this mode, all datasource names are assumed to map to JNDI datasource names of the general form "jdbc/...". If the setUsingJNDI() method is called with a value of false, the SQLConnectionManager will assume that datasource names map directly to JDBC URLs, and will attempt to obtain a JDBC Connection directly form the JDBC DriverManager.

Under no circumstances should you use the DriverManager to obtain JDBC connections in a production application! Such usage causes a new database connection to be opened for every use of the connection, and will cause enormous performance and scalability problems with your application. Make sure you use SQLConnectionManager's JNDI lookup mechanism along with a database connection pool in your container when you finally deploy your applications into production.

Add Datasource Mappings

Either of the above modes assumes that a mapping exists for the provided datasource name. These mappings are set by calling the SQLConnectionManagerBase.addDataSourceMapping(String, String) static method. This method should generally be called from within the static initializer of the application servlet class.

For example:

static
{
    setUsingJNDI(true);
 
    // Anyone asking for a connection under the name "jdbc/MyDS"
    // would cause the SQLConnectionManager to do a JNDI lookup for 
    // a datasource under the name "jdbc/Foo/MyDS-Test"
    addDataSourceMapping("jdbc/MyDS","jdbc/Foo/MyDS-Test");
}

or

static
{
    setUsingJNDI(false);
 
    // Note, if we are not using JNDI, we will need to initialize our
    // JDBC drivers in the standard way
    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
    Class.forName("oracle.jdbc.driver.OracleDriver");
 
    // Anyone asking for a connection under the name "jdbc/MyDS"
    // would cause the SQLConnectionManager to obtain a connection from 
    // the JDBC DriverManager with the URL "jdbc:odbc:northwind"
    addDataSourceMapping("jdbc/MyDS","jdbc:odbc:northwind");
    addDataSourceMapping("jdbc/HerDS","jdbc:oracle:thin:@10.0.0.1:1521:foo");
}

If a Datasource Mapping Cannot be Found

The SQLConnectionManager provides a fallback mechanism if a mapping cannot be found for a given datasource name. If a mapping cannot be found, it assumes that the datasource name is the JNDI datasource name/JDBC URL that should be used to obtain a connection. Therefore, the default mechanism is to use the literal datasource name passed to the SQLConnectionManager. This means that unless the developer adds mappings, the use of SQLConnectionManager is completely transparent.


Package the Application

Packaging of Sun ONE Application Framework applications follows the standard J2EE guidelines for Web Application aRchive files. If you've developed your application by following this guide's recommendations, you need do nothing more than use a JAR or Zip tool to archive your application directory structure into a WAR file. The WEB-INF directory and its peer directories and files should be at the root of the archive. The name of the WAR file is arbitrary, though it needs to have a .war file extension.


Deploy the Application

After configuring the application as described above, deploying a Sun ONE Application Framework application is no different than deploying any other WAR-based J2EE Web application. J2EE containers differ on the details of deploying such an application; refer to your container's deployment documentation for specifics. However, in many cases, you simply need to copy the WAR file to the container's /webapps directory.

Some additional notes on deployment:


Access a Sun ONE Application Framework Application

After deploying your Sun ONE Application Framework application, it is available to be accessed by HTTP clients. Sun ONE Application Framework applications use a standard URL format of the following general form:

http://<host + container-specific path>/<servlet context name>/<module name>/<page name>

where:

For example, given the following:

the URL to access the Hello page would be the following:

http://<host + container-specific path>/HelloWorld/greeting/Hello

The base application package name does not factor into the URL. Additionally, the requested page name must exist within the specified module. Trying to access a page outside of the requested module is illegal. For more information, see Cross-Module Navigation.

Many J2EE containers do not impose container-specific paths for access to the servlet engine. For example, Sun ONE Application Server, Apache Tomcat, or Caucho Resin would use the following URL:

http://<host>/HelloWorld/greeting/Hello

However, because some J2EE containers use a multi-tiered architecture with a conventional Web server for external access, the URL might require additional path information. For example, the example URL in iPlanet Application Server 6.x would be this:

http://<host>/NASApp/HelloWorld/greeting/Hello


Cross-Module Navigation

Cross-module navigation refers to handling a request in one module but responding with a page from another module. This scenario normally arises when moving from one logically related area of the application to another.

For the most part, the Sun ONE Application Framework runtime manages this switch automatically for you, but it is important to understand the basic mechanics. The crucial task in moving across modules is in making sure that the page ultimately rendered to the client contains references back to the module in which that page is contained. Otherwise, a request for a page in a different module would be sent back to the server, causing a security exception. Once a request crosses module boundaries, the target module's servlet will be the next servlet to handle a request from the user. For example, if a user request triggered an event in PageOne of module1, and this event forwarded the request to PageTwo in module2, module2's module servlet would be the servlet to handle the next and all subsequent user requests (until another request crossed another module boundary).

There is one subtlety of which developers should be aware when crossing module boundaries within their applications. Normally, developers use the ViewBeanManager available from the RequestContext to obtain ViewBean references. The ViewBeanManager allows developers to provide a short name for the ViewBean they want to retrieve via the getLocalViewBean() method. However, this method must prepend a package name to the provided name to derive a class name. This package name is always the package name of the module servlet which handled the request. Therefore, it is not possible to obtain a ViewBean from another module using this method; only ViewBeans within the current module are available through this shortcut method. To obtain a reference to a ViewBean in another module, use one of the other ViewBeanManager methods that expects a class or fully-qualified class name.