Sun ONE Application Framework (JATO)Deployment Guide |
Configuring a JATO Application
ViewBean Display URL Configuration
SQLConnectionManager Configuration
Setting the SQLConnectionManager to use JNDI or the JDBC Driver Manager
Interoperating with JATO Applications
JATO applications can be run in any Servlet 2.2/JSP 1.1-compliant J2EE container. This document covers the preparation of a JATO application for deployment in most J2EE containers, as well as deployment-time configuration of a JATO application. It assumes general familiarity with JATO as well as Web Application Archive (WAR) files, deployment descriptors, and your J2EE container's specific deployment method.
JATO imposes a certain structure and design pattern for applications that makes creating a scalable and maintainable application easier for developers. JATO applications are comprised of one or more modules. Each module is a functional slice or logical grouping of the overall application, and contains module-specific classes such as ViewBeans and Models. Each module also contains a module servlet, which is directly accessible by clients of the application. At least one module is required in an application, but other modules are optional and may be added at any time. Each module is a sub-package of the base application package.
Each JATO application also defines what we call the application servlet, which is the base class from which each module servlet derives. Only the module servlets are accessed by clients of the application; the application servlet serves only as a common base class for the module servlets, providing the opportunity to consolidate common application-level event handling in a single class. Module servlets allow for module-only specialization of these events. Together, these servlets form the request handling infrastructure of each JATO application.
In general, a JATO application looks like the following:
/Base application package (may be multiple directories deep) <Application servlet> <Other application classes/objects/files> /<module 1> <Module servlet> <Other module classes/objects/files> /<module 2> <Module servlet> <Other module classes/objects/files> ... /<module n> <Module servlet> <Other module classes/objects/files>
Each module is a self-contained sub-package of the overall application. Pages and objects in one module can interact with objects in other modules via cross-module navigation and/or simple class references. We cover cross-module navigation later in this document.
JATO applications are packaged like any other J2EE web-tier application in a standard Web Application Archive (WAR) file. JATO applications have certain assumptions as to where certain files will be located within the WAR file. The following is the recommended layout for JATO war files:
/<base application package> /<module 1> <Module JSP files> /<module 2> <Module JSP files> ... /<module n> <Module JSP files> /<other static resources> /WEB-INF jato.tld (the JATO tag library descriptor) web.xml (the application deployment descriptor) /classes /<base application package> <Application servlet class> <Other application classes/objects/files> /<module package 1> <Module servlet> <Other module classes/objects/files> /<module package 2> <Module servlet> <Other module classes/objects/files> ... /<module package n> <Module servlet> <Other module classes/objects/files> /lib <JATO distribution jar file> <other application jar/zip files>
As you can see, there are two parallel directory hierarchiesone for JSP files, and one for module classes. We generally recommend that these two directory hierarchies remain in parallel, although strictly, they need not. Specifically, each ViewBean is configured with the URL of its JSP peer during development, and this URL is arbitrary. However, to avoid naming collisions between modules which may be developed by different groups, we recommend maintaining the parallel directory structure illustrated above.
After packaging your JATO application into a WAR file, you will need to access the pages contained within it. JATO 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:
HelloWorld.war
greeting
<app package>.greeting.HelloViewBean
Hello.jsp
the URL to access the Hello page would be the following:
http://<host + container-specific path>/HelloWorld/greeting/Hello
Note that the base application package name doesn't factor into the URL. Also note that the request page name must exist within the specified module. Trying to access a page outside of the requested module is illegal. (We cover more about this below in the discussion about cross-module navigation.)
Many J2EE containers do not impose container-specific paths for access to the servlet engine. For example, Caucho Resin or Apache Tomcat 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 may 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 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, JATO 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 ViewBean
s
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.
In addition to packaging a JATO application, it must be configured before it can be deployed.
The JATO 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>:<param name>
For example:
<context-param>
<param-name>jato:fooapp.module1.Module1Servlet:foo</param-name>
<param-value>bar</param-value>
</context-param>
The specified class name may 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 may only contain a single asterisk, and will match any classes whose class name matches the string before and/or after the asterisk.
The specified 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 will be converted to the appropriate type for
the setter method. If the type cannot be converted, an error message will be
written to the servlet context indicating the exception.
The following module servlet parameters are currently available:
Parameter Name | Type | Description |
---|---|---|
moduleURL | String |
The URL for the module servlet. See detailed description below. |
debug | boolean |
Enables or disables request parameter debug information sent to the servlet context log and the rendered HTML page. Note, some containers may not send this debug output in the client HTML stream. |
showMessageBuffer | boolean |
Enables or disables showing the application message buffer at the bottom of the rendered HTML page |
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 JATO application is rendered, the JATO tag library renders references to the current module's URL into the HTML output, so that submitted forms or clicked 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 JATO infrastructure. Additionally, in order 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. These restrictions are the following:
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 JATO HTML page are relative URLs. Because of the standard JATO URL format, which specifies the page name at the end of the URL path, the moduleURL must be configured like so:
<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 packageit could be any arbitrary name. Although this is what we recommend 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,
fooapp
fooapp.module1
a basic JATO application would have the following deployment descriptor (including the taglib declaration):
<web-app> <context-param> <param-name>jato:fooapp.module1.*:moduleURL</param-name> <param-value>../module1</param-value> </context-param> <servlet> <servlet-name>Module1Servlet</servlet-name> <servlet-class>fooapp.module1.Module1Servlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Module1Servlet</servlet-name> <url-pattern>/module1/*</url-pattern> </servlet-mapping> <taglib> <taglib-uri>/WEB-INF/jato.tld</taglib-uri> <taglib-location>/WEB-INF/jato.tld</taglib-location> </taglib> </web-app>
Adding a second module named Module2
to this application would
result in the following:
<web-app> <context-param> <param-name>jato:fooapp.module1.*:moduleURL</param-name> <param-value>../module1</param-value> </context-param> <context-param> <param-name>jato:fooapp.module2.*:moduleURL</param-name> <param-value>../module2</param-value> </context-param> <servlet> <servlet-name>Module1Servlet</servlet-name> <servlet-class>fooapp.module1.Module1Servlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Module1Servlet</servlet-name> <url-pattern>/module1/*</url-pattern> </servlet-mapping> <servlet> <servlet-name>Module2Servlet</servlet-name> <servlet-class>fooapp.module2.Module2Servlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Module2Servlet</servlet-name> <url-pattern>/module2/*</url-pattern> </servlet-mapping> <taglib> <taglib-uri>/WEB-INF/jato.tld</taglib-uri> <taglib-location>/WEB-INF/jato.tld</taglib-location> </taglib> </web-app>
Each ViewBean in a JATO 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 may 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 (but note, 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: We do not generally recommend using this mechanism to override ViewBean display URLs, as it introduces some overhead. Instead, we recommend that the affected ViewBeans be recompiled with the appropriate display URL directly if possible.
Each JATO application will typically contain a class called SQLConnectionManagerImpl
in its base application package. This class is an application-specific implementation
of JATO's SQLConnectionManager
interface, and an instance of this
manager object will be available in a JATO 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 JATO objects. The basic usage
of the SQLConnectionManager
is that callers ask it for a JDBC Connection
using what's 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 wish 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 wishes 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 informationhost, 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
may allow arbitrary names after the standard "jdbc/"
prefix, where another container may 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 serious problem.
This is where JATO's SQLConnectionManager
seeks to provide assistance.
The SQLConnectionManager
contains its own datasource mapping mechanism,
which allows the JATO 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 JATO application to run in a container that doesn't support
the JDBC 2.0 Standard Extension, albeit without the benefit of connection pooling.
As we 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 a JATO 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; we recommend calling this method
from the static initializer of your application-specific SQLConnectionManagerImpl
class.
Calling the setUsingJNDI()
method with a value of true
will enable JNDI lookups of JATO datasource names. In this mode, all JATO 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 JATO datasource names
map directly to JDBC URLs, and will attempt to obtain a JDBC Connection
directly form the JDBC DriverManager
.
WARNING:
Unless you are using specially-written connection pooling JDBC drivers, disabling
the use of JNDI results in the use of non-pooled database connections. This
will cause the SQLConnectionManager
to allocate a new connection
whenever one is requested, and is an extremely expensive operation. This mode
should absolutely not be used in a production deployment.
Either of the above modes assumes that a mapping exists for the provided JATO
datasource name. These mappings are set by calling the SQLConnectionManagerBase.addDataSourceMapping(String,
String)
static method. This method is marked protected
,
and should generally be called from within the static initializer of the application-specific
SQLConnectionManagerImpl
class.
For example:
static { setUsingJNDI(true); // Anyone asking for a connection under the JATO 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 JATO 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"); }
The SQLConnectionManager
provides a fallback mechanism if a mapping
can't be found for a given JATO datasource name. If a mapping can't be found,
it assumes that the JATO datasource name is the JNDI datasource name/JDBC URL
that should be used to obtain a connection. Therefore, if you choose JATO datasource
names appropriately, you may not need to add a mapping.
After configuring the application as described above, deploying a JATO application is no different than deploying any other WAR-based J2EE application. J2EE containers differ on the details of deploying such an application; we refer you to the deployment documentation for your container.
Some notes on deployment:
/WEB-INF
directory). This is not a servlet
2.2 specification recommendation, but we believe it has several advantages.
First, it aids troubleshooting efforts by providing an exact image of the
source that is actually being used in the application. Second, it ensures
that critical fixes can easily be made to deployed applications without requiring
a full development/source control environment.SQLConnectionManager
..jsp
files from external clients if possible, since these
files are only meant to be accessed through JATO servletsBecause JATO applications are just J2EE Web applications, other Web applications can interoperate with them. The techniques for interoperation with JATO depend on how the JATO application components will be accessed.
By external application, we mean a completely separate application in a different servlet context, application server, web server, J2EE or non-J2EE container, etc.
For the most part, this type of interoperation is the same as it would be for any other web applicationJATO applications are invokable through a standard HTTP URL. If you wish to pass parameters to a JATO application, you can simply append query parameters to the HTTP invocation and access these from within the invoked JATO component.
However, this type of invocation will result in a first-touch request, a request that doesn't invoke a request event handler. In most cases, this is fine. In some cases, though, you may wish to simulate a JATO button press or HREF click. If so, you will need to include at least one name-value pair as a query parameter or posted value.
Each request resulting from a JATO button or HREF includes a parameter
that signals to JATO's request handling infrastructure that it should invoke
a request event handler (a handle<child>Request()
method).
The name of this parameter is the fully-qualified name of the button or HREF.
For example, a button name SubmitButton
on PgFoo
would
generate a parameter like this:
PgFoo.SubmitButton=Submit
Therefore, to simulate this button press, you would send an HTTP request to the JATO application that looks something like this:
http://<host>/fooapp/module1/PageFoo?PageFoo.SubmitButton=Submit&...
This would result in the handleSubmitButtonRequest()
method being invoked on PageFoo
. Note that if you wish to populate
any of the other fields on the same page, as most request event handler methods
would expect, you need to append additional parameters to the query string or
posted content.
If you wish to invoke a request event handler within a child TiledView of a page, you need to also include a row number in the parameter name:
PageFoo.FooTiledView[3].SubmitButton=Submit
This would invoke the request event handler in the TiledView named
FooTiledView
as if the button press had occurred on the fourth
tile (tile numbers are zero-based).
In some cases, a single web application may contain both JATO and non-JATO J2EE components, such as non-JATO servlets and JSPs. The advantage of interoperating with JATO from within the same application is that the application components can share session and other objects via the request and servlet context attributes. This makes it much easier to move seamlessly between components.
In most situations, you can simply follow the same basic guidelines
for interoperating with JATO from an external application. Note, however, that
you will instead need to forward to or include the JATO component in the request
using the standard javax.servlet.RequestDispatcher
mechanism. When
forwarding or including in this manner, you must be sure to request the standard
JATO URL and not any of the JATO JSPs directly:
String target="/fooapp/module1/PageFoo?PageFoo.SubmitButton=Submit&..."; RequestDispatcher dispatcher= request.getServletContext().getRequestDispatcher(target); dispatcher.forward(request,response);
This is currently our only recommended technique for interoperating with JATO within the same application, as it allows the JATO module servlet to establish a proper context for handling the request. We cannot recommend other techniques, such as attempting to instantiate and use a ViewBean directly, because they would require a significant amount of complex manipulation of the JATO infrastructure (which may change from one JATO version to another). In the future, based on user feedback, we may provide a simpler, API-based mechanism for this style of interoperation.