This section discusses some extension points that affect the overall framework, not necessarily one particular layer. Arguably the most important of these is the PlugIn mechanism.
The Struts framework provides a mechanism to allow components to be plugged in and loaded dynamically. This feature was added in Version 1.1 and is supported through the use of the org.apache.struts.action.PlugIn interface. Any Java class can function as a plug-in, as long as it implements the PlugIn interface.
A plug-in is simply any Java class that you need to initialize when the Struts application starts up, and destroy when the application shuts down.
The PlugIn interface contains two methods, as shown in Example 9-1.
public interface PlugIn {
/**
* Notification that the specified application module is being started.
*/
public void init(ActionServlet servlet, ApplicationConfig config)
throws ServletException;
/**
* Notification that the application module is being shut down.
*/
public void destroy( );
}
During startup of a Struts application, the ActionServlet calls the init( ) method for each PlugIn that is configured; the framework supports configuration of one or more PlugIns for each application. Initialization routines that your plug-in needs to perform should be done during the init( ) method. This is a good time to initialize a database connection or establish a connection to a remote system, for example.[1]
[1] You also can initialize a database connection through the use of a datasource.
The second method that your plug-in must implement is the destroy( ) method. The framework calls this method when the application is being shut down. You should perform any necessary cleanup during this time. For example, this is the perfect time to close database connections, remote sockets, or any other resources that the plug-in is using.
Let's provide a concrete example of how to use the Struts framework's PlugIn mechanism. Suppose that your application needs the ability to communicate with an EJB tier. One of the first things you must do before that can occur is to get a reference to the Java Naming and Directory Interface (JNDI) service. JNDI enables clients to access various naming and directory services, such as datasources, JavaMail sessions, and EJB home factories. Example 9-2 illustrates a simple example of acquiring an InitialContext for a JNDI service using the Struts PlugIn mechanism.
package com.oreilly.struts.storefront.framework.ejb;
import java.util.Hashtable;
import javax.naming.InitialContext;
import javax.naming.Context;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.config.ApplicationConfig;
import org.apache.struts.action.PlugIn;
import javax.servlet.ServletException;
public class JNDIConnectorPlugin implements PlugIn {
private String jndiFactoryClass;
private String jndiURL;
private Context initCtx = null;
public JNDIConnectorPlugin( ) {
super( );
}
public void init(ActionServlet servlet, ApplicationConfig config)
throws ServletException{
// Get the host and port where the JNDI service is running
jndiFactoryClass = servlet.getInitParameter("jndi-factory-class");
jndiURL = servlet.getInitParameter("jndi-url");
try{
Hashtable props = new Hashtable( );
// The EJB spec also allows these to be read from the jndi.properties file
props.put( Context.INITIAL_CONTEXT_FACTORY, jndiFactoryClass );
props.put( Context.PROVIDER_URL, jndiURL );
initCtx = new InitialContext(props);
}catch( Exception ex ){
throw new ServletException( ex );
}
// Store the JNDI Context into the ServletContext
servlet.getServletContext( ).setAttribute( "Storefront.InitCtx", initCtx );
}
public void destroy( ){
try{
if ( initCtx != null ){
initCtx.close( );
initCtx = null;
// No need to remove from ServletContext because app is being shut down
}
}catch( Exception ex ){
ex.printStackTrace( );
}
}
}
When the framework calls the init( ) method of the JNDIConnectorPlugin class, the plug-in creates an InitialContext object and stores it into the ServletContext. This allows the JNDI InitialContext to be used by the entire application, when needed.
This is just a simple example; there are many possible uses for the PlugIn mechanism. For example, the Validator framework, which we'll discuss in Chapter 11, uses the PlugIn mechanism to initialize the validation rules for an application.
The plug-in must be declared in the Struts configuration file in order for the framework to be aware of it and initialize it at startup. It's specified in the configuration file using the plug-in element:
<plug-in
className="com.oreilly.struts.storefront.framework.ejb.JNDIConnectorPlugin"/>
You also can pass properties to your PlugIn class by using the set-property element.
|
For more information on configuring the plug-in element or passing properties to an instance, see Section 4.7.2 in Chapter 4.
One of the biggest changes to Version 1.1 of the Struts framework is the org.apache.struts.config package. This package contains all of the classes that are used as in-memory representations of the information stored in the Struts configuration file. They represent everything from Action configurations to PlugIn configurations.
If you look back at Chapter 4, you'll see that most of the configuration elements in the Struts configuration file allow you to supply a fully qualified Java class name for the configuration class through the className attribute. This gives you the freedom to customize the configuration element and pass additional information.
For example, suppose that you want to pass an additional parameter to your Action classes. By default, the ActionMapping class, which extends ActionConfig from the config package, is used. To pass an additional parameter called ssl-required that controls whether HTTP or HTTPS is used, you can extend the ActionMapping class and configure this extension through the className attribute. The ability to extend the Struts configuration elements through this mechanism makes the framework extremely extensible and flexible enough to meet just about any application need.