Introduction


This document discusses how you can set up a JDeveloper 10g workspace in order to use the multiple configuration features of Apache Struts. The use of multiple configuration files for the same application is a new feature added to Struts version 1.1 to help application developers better structure their sites and to ease the burden of maintenance for large applications. Multi configuration actually encompasses two different styles of usage:



  • Peer configurations - where one large application struts-config.xml file has been arbitrarily split up into two or more peer configuration files. The struts-example application is an example of this.
        <servlet>
          <servlet-name>action</servlet-name>
          <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
          <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml</param-value>
          </init-param>
          ...
        </servlet>

  • Sub-applications - where a more logical division of an application exists and where there is a master application configuration file and then one or more sub-application configuration files which are children of the main application and accessed at runtime using a path like syntax based on the sub-application name..
        <servlet>
          <servlet-name>action</servlet-name>
          <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
         <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
         </init-param>
         <init-param>
            <param-name>config/child1</param-name>
            <param-value>/WEB-INF/child1-config.xml</param-value>
         </init-param>
         <init-param>
            <param-name>config/child2</param-name>
            <param-value>/WEB-INF/child2-config.xml</param-value>
         </init-param>
          ...
        </servlet>

This document assumes that you are already familiar with the basic use of Struts, Struts multiconfig usage and of JDeveloper 10g as a Struts development environment.


JDeveloper 10g and Multiple Configurations


The reason for producing this document is to help developers around a restriction in the initial release of the JDeveloper 10g product. With this release of the product only one Struts page flow diagram can exist in each project. Attempting to use multiple page flow diagrams within a project can lead to unpredictable results and diagram corruption. As a result, applications which want to utilize peer or sub configurations will need to be organized in a special way to allow full page flow diagram functionality for each configuration file. The exact steps are outlined below, both for new applications and existing applications.


Creating a new Multiple Configuration Workspace


When creating a new multi config application you will need to plan in advance how many configuration files you will be using. In this example we'll use a total of three configurations, struts-config.xml as the parent configuration and two sub applications child1-config.xml and child2-config.xml. The principals of maintaining this projects in sync revolve around the use of a common HTML root and a common J2EE application name shared by all the projects. So lets step through the creation process for our workspace.



  1. Starting with an existing workspace or a newly created on if required, create three empty projects, in this case called parent, child1 and child2. It is best to create all of these under a common workspace directory, with each project in it's own sub-directory.
  2. At the same level as each of the project directories in the file system, create a public_html directory so you end up with a directory structure as follows:

    • Workspace directory

      • Parent
      • Child1
      • Child2
      • public_html

  3. Now edit the project properties of each project. Under the Common >> Input Paths node of the project properties, set the HTML Root Directory property to point to the common public_html directory
  4. Also in the project properties, choose the Common >> J2EE node and set each of the projects to use the same application and context root names e.g. multiApp and multiApp-context-root respectively. As you set the second and subsequent projects to share the same J2EE information you will receive a warning that another project shares the same name, you can ignore this warning and press Yes to continue.
  5. At this point save the workspace.
  6. Now you need to add Struts and your chosen view technology to the technology scope of each project. It is important that you do this to the parent project last.
    Again from the project properties dialog choose Common >> Technology Scope. Shuttle Struts and your view technology (e.g. JavaServer Pages (JSP) and Servlets) into the selected technologies list. When you OK the project properties dialog you will see a few files have been added to the project. Now save the Workspace. It is important to rename the struts-config.xml to the required name in each project as you go. So rename the xml file using File >> Rename, then save the workspace again and then continue to the next project.
  7. Once all of the projects have been set up for Struts you will need to edit the web.xml to define how each of the configurations will be used. As all of the projects share the same HTML root directory, they will also be sharing the same web.xml. As a result you have to be careful when updating web.xml and it is advisable to only ever update it from one place e.g. the parent project in this case. In this example we have three configurations, two of which are sub-applications. Here is the relevant part of the example web.xml file:
    <?xml version = '1.0' encoding = 'windows-1252'?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
       <description>Empty web.xml file for Web Application</description>
       <servlet>
         <servlet-name>action</servlet-name>
         <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
         <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
         </init-param>
         <init-param>
            <param-name>config/child1</param-name>
            <param-value>/WEB-INF/child1-config.xml</param-value>
         </init-param>
         <init-param>
            <param-name>config/child2</param-name>
            <param-value>/WEB-INF/child2-config.xml</param-value>
         </init-param>
    ...

    This Struts application is set up then with two sub applications which will be accessed through the use of /child1/ and /child2/ in the URI.
  8. Next you will need to flag to the projects that they are all dependent on each other - this is important to ensure that all of the files are correctly deployed when it comes to running your application and for resolving common files such as a shared ApplicationResources.properties file. Again this is done through the project properties dialog using the Common >> Dependencies node.
  9. Finally when it comes to running the application, ensure that you run the parent project first. Running an action on one of the child configurations before the parent can cause the parent not to run subsequently, although this can be corrected by changing the J2EE application and context root names in all of the projects and rerunning the parent project

Creating a Working Multiple Configuration Workspace from an Existing WAR file


If you have an existing code base or application which uses multiple configurations which you want to edit through JDeveloper you can import a WAR file of the application into an empty JDeveloper Workspace using the following steps :



  1. In the Application Navigator create an empty workspace, uncheck the Create New Empty Project checkbox if this is set.
  2. Select the workspace and choose File -> Import.
  3. In the Import dialog select the WAR file file you wish to consume. The Create Project from War File Wizard will appear.
  4. In step 1 define the project name as required and it's location. (Bear in mind that like starting from scratch you will be creating several projects to hold the various configurations).
  5. In step 2 of the wizard, define the name of the WAR file that you wish to import and define the location for the HTML root. As in the custom created projects, this HTML Root will be shared so it's probably a good idea to define it at the same level as your imported project, rather than under the imported project directory itself.
  6. Once the WAR file is imported expand the project WEB-INF folder. Examine the web.xml file to see which is the primary Struts configuration in the servlet definition, you'll want to keep this primary XML file in this project and move any others into projects of their own. So in the case of the struts-example.war that is shipped as a sample with Struts, and available in the /jakarta-struts/webapps directory under the JDeveloper distribution, the configuration setting in the web.xml looks like this:
      <!-- Action Servlet Configuration -->
      <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
          <param-name>config</param-name>
          <param-value>/WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>

  7. So in this case the struts-config.xml should remain in the import project and the struts-config-registration.xml removed from the import project using the remove button or the remove option on the file menu (not it needs to be removed from the project but not deleted from disk)..
  8. Create a new empty project for each of the extra configuration files.
    For each file:

    • In the project properties dialog select the Common -> Input Paths node of the tree and set the HTML Root Directory to the top level HTML Root directory you defined when using the WAR import wizard
    • The select the Common -> Dependencies node in the preferences dialog and check the dependency to the project imported from the WAR file.
    • Then select the Common -> J2EE node in the preferences dialog and set both the J2EE Web Application Name and the J2EE Web Context Root to the same values as used by the project imported from the WAR file. Ignore the unique name warning that pops up.
    • Then select the libraries node of the project properties tree and add JSP Runtime and Struts Runtime to the selected libraries list.
    • Choose Add to ... from the file menu to add the existing web.xml and secondary Struts configuration file to the new project
    • Back in the imported project edit the project properties and add this new project as a dependency of the imported one.

Once you have completed this process for each sub configuration you will be able to open a page flow diagram up in each project. You may choose to move JSP files etc to the relevant project that they apply to, however, there is no need to specifically do this as JDeveloper will work out where the files are if you try and drill down into them from the page flow diagram.


Running Child Application Page Flows


In the situation where you have sub-applications (such as in our example child1 and child2), you will be able to run Actions on the parent page flow diagram, or any peer application of the parent directly. However, if you attempt to run an Action from a sub application page flow diagram, it will fail with HTTP 400 (page cannot be found) error. This is because the URL that the runner will construct for the Action will not include the sub application path. For instance the url: http://localhost:8988/multiApp-context-root/child1_index.do may be generated rather than the correct http://localhost:8988/multiApp-context-root/child1/child1_index.do.


There are several approaches you can take to making it possible to run from a child application diagram. Take the situation where the parent project has a single entry point that the user always has to traverse, for instance a logon page. In this case using the context menu on the parent diagram set that page as the default run target for the parent diagram. This has the side effect of setting the Action unknown attribute to true for that action, so if you run from any of the other diagrams where an invalid Action URL is generated, Struts will redirect you to the application start point automatically.


Note that this will only work successfully if your Action names are unique across all of the sub applications.


However, you may wish to unit test just the flow within a particular sub application. If this is the case then you will need to adopt the following procedure.



  1. In the child configuration project, create a new JSP page called launch.jsp or similar.
  2. Edit this new page and add the following tag to the body:
    <jsp:forward page="/child1/child1_index.do"/>
    (Change the sub-application path and entry action as appropriate for your application)
  3. Edit the project properties for the child application project and in the Profiles >> Runner settings set the Default Run Target to your launch JSP and uncheck the Attempt to Run Active File Before Default checkbox.
  4. Now when you select run from the diagram this launch page will run, which in turn will construct the correct URL for your sub app.
  5. If you need to test different Actions within the child application, simply update the <jsp:forward> tag on this launch page as required.

Conclusion


Although the initial release of JDeveloper 10g does not seamlessly support multiple configuration Struts applications, with a little care it is entirely possible to use the product with such applications. In the next version of JDeveloper 10g it will be possible to dispense with the multiple project requirement, although structuring the workspace in this way may have benefits if multiple developers are working on different sections of the application.

Posted by 아름프로



alt="HiveMind: What's it all about?">







April 2005



Discussion








HiveMind, one of the newer Jakarta subprojects at The Apache Software Foundation,
is described as a framework for creating applications, not an application,
or even an application server, itself. Howard Lewis Ship created HiveMind
while working on WebCTs enterprise e-learning product, Vista. Howard
also created Tapestry, a very popular web development framework. Tapestry paved
the way for component-based web application frameworks and HiveMind is also
beginning to make some waves of its own. In fact, the soon-to-be-released version
of Tapestry actually uses HiveMind extensively. In a recent email, Howard recently
explained to me his thoughts about HiveMind as:



My vision for HiveMind is drawn from the workings of a bee hive or ant colony
(or, for Star Trek fans, the Borg collective). Each individual member has
a very limited job to do, and does its work without concern with what any
other member is doing. And yet, a bee hive or ant colony will expand physically,
collect food, produce new generations of workers, fight against intruders
... do all the things necessary to survive, as if it were a single living
entity (Douglas R. Hofstadter's book "Gödel Escher Bach: An Eternal Golden
Braid" entertainingly discusses this concept at length). This directed
behavior on the part of the bee hive is emergent behavior: the individual
bees of the hive don't have or need an understanding of the greater goals
in order to pursue their part; they just react to their environment, and
the constant stream of chemical signals that flow about the hive.



Likewise, in HiveMind, we can set to task a good number of simple services,
and by letting them collaborate and share information, they can produce the
desired application behavior. But at its heart, HiveMind is about the simplicity
that comes from breaking complex problems into small pieces -- the heart of
Object Oriented development.


As far as an overview, that pretty much sums it up. HiveMind is all about
designing systems as small, testable services which interact with one another
to make up an application. In this article, we will explore some of the key
features of HiveMind by developing a user registration service to be used by
an online application.


The Objective


The requirements for the registration service are:



The user registration service will allow the creation of user accounts based
upon an email address. The email address used to create an account must
be unique within the system. As part of the registration process, an email
should be sent to the users email address containing a system-generated
password for account verification purposes. The user accounts should be stored
in a database.



To follow good programming practices, we define an interface for our service.



public interface RegistrationService
{
public void registerUser( String emailAddress )
throws UserAlreadyExistsException;
}

Since our requirements are relatively simple, our service interface also turns
out to be quite simple. Here, we allow the creation of a user account using
only an email address. If a user account already exists in the system with
the supplied email address, we will throw a UserAlreadyExistsException. Now
that weve
defined what our service looks like, we have to let HiveMind know about our
service using a module descriptor file (discussed in more detail later). As
of now, we only have one service in our HiveMind module, so our descriptor
file will look like:




id="registrationService"
interface="hivemind.example.article.service.RegistrationService"/>


Here, weve created a module with id hivemind.example.article containing
one service with id registrationService. Our implementation class
will likely need to collaborate with some other classes in order to fulfill
all of these requirements. First, since we will be dealing with persistent
user account information, we should create a User class to contain the data.
The User class should contain properties for the email address and password
(should be somewhat easy to imagine, so we wont list the code). Next,
we will need some way of asking if a user account with the requested email
address is already in the database. We will also need to be able to add a user
account to the database. These actions logically belong together, as they are
both database-related and pertain to the User class.



public interface UserRepository
{
public User getUserByEmailAddress( String emailAddress );
public void addUser( User user );
}

We should also provide a mechanism for creating new User objects.



public interface UserFactory
{
public User createUser( String emailAddress );
}

Finally, we will need a way to send account verification emails to our users
once their account is created.



public interface EmailService
{
public void sendEmail( String to, String subject, String body );
}

We now have three more services to tell HiveMind about. Our module descriptor
now looks like:




id="registrationService"
interface="hivemind.example.article.service.RegistrationService"/>
id="emailService"
interface="hivemind.example.article.service.EmailService"/>
id="userFactory"
interface="hivemind.example.article.domain.factory.UserFactory"/>
id="userRepository"
interface="hivemind.example.article.domain.repository.UserRepository"/>



Now that weve created abstractions for most the auxiliary actions we
need to perform, we can get down to writing the core of our service implementation.
With these abstractions in place, the logic is quite simple.



public class RegistrationServiceImpl implements RegistrationService
{
private UserRepository userRepository;
private UserFactory userFactory;
private EmailService emailService;

public void registerUser(String emailAddress)
throws UserAlreadyExistsException
{
if(userRepository.getUserByEmailAddress(emailAddress) == null)
{
User user = userFactory.createUser(emailAddress);
userRepository.addUser(user);
emailService.sendEmail(emailAddress,
"Account Registration",
"Your new account password is \"" +
user.getPassword() +
"\".");
}
else
{
throw new UserAlreadyExistsException();
}
}

public void setUserFactory(UserFactory userFactory)
{
this.userFactory = userFactory;
}

public void setUserRepository(UserRepository userRepository)
{
this.userRepository = userRepository;
}

public void setEmailService(EmailService emailService)
{
this.emailService = emailService;
}
}

Notice how we are relying upon the userRepository, userFactory, and emailService
member variables to be non-null. Many of you will find this to be quite disturbing,
because your years of experience have taught you that this will likely cause
a NullPointerException at some point. Just relax. Were going to let
HiveMind provide our dependencies for us.


Dependency Injection


When it comes to collaborating with other objects, we have a few choices of
how we obtain the references to those objects. We can create the objects ourselves.
We can lookup the objects in some sort of repository or registry. Or, we can
just have them handed to us by some external entity. HiveMind uses the latter
approach and the concept is known as dependency injection (a.k.a.
Inversion of Control). Dependency injection can be described using The
Hollywood Principle, or the notion of dont call us; well
call you. In our example, were letting HiveMind provide us with
our dependencies by exposing setter methods for them or by using setter-based
injection. Dependencies can also be injected via constructor parameters
and that is known as constructor-based injection. With HiveMind
setter-based injection and constructor-based injection are not mutually exclusive.
You are free to mix and match them as you see fit. Most people stick with just
one type for consistency; which type you choose is up to you.


Using dependency injection provides many benefits. One of which is the ability
to unit test your implementation very easily. Since our objects arent
creating the dependencies they need, we have control over what dependent objects
they use. We can supply them with mock objects which behave precisely
as we need in order to test certain conditions. The accompanying code contains
JUnit test cases which use JMock to do just that. For now, though, lets
see how to tell HiveMind about our implementation class.




id="registrationService"
interface="hivemind.example.article.service.RegistrationService"/>
id="emailService"
interface="hivemind.example.article.service.EmailService"/>
id="userFactory"
interface="hivemind.example.article.domain.factory.UserFactory"/>
id="userRepository"
interface="hivemind.example.article.domain.repository.UserRepository"/>


class="hivemind.example.article.service.impl.RegistrationServiceImpl"/>




Here, weve instructed HiveMind that our implementation class is to
be used for the registrationService service points implementation.
Since there are service points which implement the interfaces for all of our
dependencies, they will be automatically injected. Note, however, that the
dependencies can only be autowired if there is no ambiguity about
which service point to use (outside the scope of this article). So far, we
havent implemented the other three services in our module. The email
service would be fairly easy to write using JavaMail, so we wont include
it. The user factory implementation would be responsible for instantiating
a User object and setting its email address and password (randomly generated
of course). That, as you can imagine, would be quite trivial also. The user
repository, however, will require a bit more effort. Remember that the user
repository is an abstraction for the database that contains the user data.
We could develop the code using JDBC, but writing object/relational mapping
code by hand can be quite tedious. So, well use Hibernate instead. We
could have just as easily used JDO or some other O/R mapping tool, but Im
more familiar with Hibernate. Our Hibernate implementation of the UserRepository
interface might look like:



public class HibernateUserRepository implements UserRepository
{
private Session session;

public User getUserByEmailAddress( String emailAddress )
{
String hql = "select u from User u where u.emailAddress = ?";
Query query = session.createQuery( hql );
query.setString( 0, emailAddress );
return ( User )query.uniqueResult();
}

public void addUser( User user )
{
session.save( user );
}

public void setSession( Session session )
{
this.session = session;
}
}

Again, we must inform HiveMind about our implementation class:




id="registrationService"
interface="hivemind.example.article.service.RegistrationService"/>
id="emailService"
interface="hivemind.example.article.service.EmailService"/>
id="userFactory"
interface="hivemind.example.article.domain.factory.UserFactory"/>
id="userRepository"
interface="hivemind.example.article.domain.repository.UserRepository"/>
id="hibernateSession"
interface="org.hibernate.Session"/>



class="hivemind.example.article.service.impl.RegistrationServiceImpl"/>









Notice, however, that we have a service point hibernateSession of
type org.hibernate.Session (were using Hibernate3 so that we dont
have to catch all of the checked exceptions) which has no implementation defined.
We cant just tell HiveMind to construct an object which implements the
Session interface directly. Hibernate requires that you use a Hibernate SessionFactory
to create Session objects. So, how do we tell HiveMind that it needs to construct
a Configuration object, which can construct a SessionFactory object, which
can construct a Session object? We must define a service implementation
factory.


Service Implementation Factories


A service implementation factory is responsible for constructing the implementation
object for a service. You may not have known it, but we were using a service
implementation factory already when we defined our services implementations.
When we used the element in our module descriptor, HiveMind
used its built-in BuilderFactory service implementation factory, which is responsible
for performing the dependency injection and various other tasks, to construct
our implementation object. If you wish to use a different service implementation
factory, you must provide the service id of a service which implements the
ServiceImplementationFactory interface. Heres what our Hibernate Session
factory looks like (were using the HiveMind 1.1 API here):



public class HibernateSessionFactory
implements ServiceImplementationFactory, RegistryShutdownListener
{
private SessionFactory sessionFactory;
private ThreadEventNotifier threadEventNotifier;
private boolean updateSchema = true;
private Log log;

public void init()
{
log.debug( "Initializing Hibernate SessionFactory..." );
Configuration config = new Configuration();
config.configure();
if( updateSchema )
{
log.debug( "Updating database schema..." );
new SchemaUpdate( config ).execute( true, true );
}
sessionFactory = config.buildSessionFactory();
}

public Object createCoreServiceImplementation
( ServiceImplementationFactoryParameters params )
{
log.debug( "Creating Hibernate Session..." );
Session session = sessionFactory.openSession();
threadEventNotifier.addThreadCleanupListener(new SessionCloser(session));
return session;
}

public void registryDidShutdown()
{
log.debug( "Closing Hibernate SessionFactory..." );
sessionFactory.close();
}

public void setThreadEventNotifier(ThreadEventNotifier notifier)
{
this.threadEventNotifier = notifier;
}

public void setLog( Log log )
{
this.log = log;
}

public void setUpdateSchema( boolean updateSchema )
{
this.updateSchema = updateSchema;
}

private class SessionCloser implements ThreadCleanupListener
{
private final Session session;

public SessionCloser( Session session )
{
this.session = session;
}

public void threadDidCleanup()
{
log.debug( "Closing Hibernate Session..." );
session.close();
threadEventNotifier.removeThreadCleanupListener( this );
}
}
}

Theres a lot here, so well just go through the methods one-by-one.
First, the init() method is used to initialize the Hibernate SessionFactory,
using only the default hibernate.cfg.xml file (and optionally
updating the schema). Then, theres the createCoreServiceImplementation()
method. That method (from the ServiceImplementationFactory interface) is responsible
for creating the actual Hibernate Session object. Notice that it registers
a listener object with the ThreadEventNotifier which closes the Hibernate Session
upon thread cleanup (well talk more about this a bit later).
Then, we have the registryDidShutdown() method, which is from the RegistryShutdownListener
interface. That method will close out the SessionFactory when the HiveMind
registry is shutdown (more on this later also). Now that we have implemented
a service implementation factory for Hibernate Sessions, we have to let HiveMind
know about it:




id="registrationService"
interface="hivemind.example.article.service.RegistrationService"/>
id="emailService"
interface="hivemind.example.article.service.EmailService"/>
id="userFactory"
interface="hivemind.example.article.domain.factory.UserFactory"/>
id="userRepository"
interface="hivemind.example.article.domain.repository.UserRepository"/>
id="hibernateSession"
interface="org.hibernate.Session"/>
id="hibernateSessionFactory"
interface="org.apache.hivemind.ServiceImplementationFactory"/>



class="hivemind.example.article.service.impl.RegistrationServiceImpl"/>









class="hivemind.example.article.hibernate.HibernateSessionFactory"
initialize-method="init"/>




One restriction placed upon us by Hibernate is that the Session objects are
not thread-safe. Therefore, we should only be using one Session per thread.
How do we tell HiveMind that it is supposed to instantiate a new Session for
each thread? We use a different service lifecycle model.


Service Lifecycle Models


HiveMind introduces a somewhat unique concept called service lifecycle models.
A service lifecycle model controls when a services implementation (the
interceptor stack, core implementation object, and all its dependencies) is
created. Furthermore, a service lifecycle model can also control which implementation
object is used for a specific invocation. The registry delegates to a service
points service lifecycle model when it needs to return the service object
requested by a client. Many service lifecycle models (all except primitive)
actually return lightweight proxy objects which implement the service interface
rather than the actual implementation. At runtime, when a method is called
on the lightweight proxy, it then constructs the implementation and delegates
all invocations to it. Lets take a look at each of the service lifecycle
models:



  • Primitive – the simplest service model. The service implementation
    is constructed upon first reference (when the registry is asked for it) and
    destroyed upon registry shutdown.

  • Singleton – the default service model. The service implementation
    is constructed upon first invocation and destroyed upon registry shutdown.

  • Threaded – the service is constructed upon first invocation and to
    be used only within the calling thread and is destroyed upon thread cleanup.
    A service implementation class may optionally implement the Discardable interface
    to receive notifications of when it is discarded by the threaded service
    model.

  • Pooled – the service is obtained from a pool upon first invocation
    and returned to the pool up on thread cleanup. Optionally, the service implementation
    class may implement the PoolManageable interface to be receive notifications
    of when it is activated (bound to a thread) or deactivated (unbound from
    a thread and returned to the pool).


Youre probably wondering what was meant by thread cleanup from
the description of the threaded and pooled service models. HiveMind is specifically
targeted for web applications and other multi-threaded environments. HiveMind
can allow specific services to hold thread-specific state (for the duration
of a single web request, typically) and needs to be informed when to free that
state data (that is, and the end of the web request). To inform these services
that the thread-specific state needs to be freed, HiveMind provides the ThreadEventNotifier
service, which notifies listeners of thread cleanup events. A
thread cleanup event means that the current thread is effectively terminating
its execution and any thread-specific information should be cleaned up. The
threaded and pooled service models register themselves with the ThreadEventNotifier
as ThreadCleanupListeners, so that they may discard or return to the pool the
service implementations, respectively. How does the ThreadEventNotifier know
when to fire the thread cleanup events? The registry contains a convenience
method called cleanupThread(), which tells the ThreadEventNotifier to fire
the events. In web applications, the logical time to call cleanupThread() is
right before the response is returned to the client. HiveMind comes with a
servlet filter which does exactly that. For stand-alone , multi-threaded applications,
its a little more difficult to figure out how and when to call cleanupThread().
Ideally, you would have a class responsible for executing logic (Runnable objects)
in other threads (JDK 5 calls these Executors). That class would be responsible
for calling cleanupThread() after each execution. Another lifecycle event,
which weve already seen, is fired when the HiveMind registry is shutdown.
All RegistryShutdownListeners registered with the ShutdownCoordinator will
recive notifications. Now that you know how service models work, youre
probably wondering how to use them. That turns out to be the easy part. You
specify which service model you wish to use in the element:




id="registrationService"
interface="hivemind.example.article.service.RegistrationService"/>
id="emailService"
interface="hivemind.example.article.service.EmailService"/>
id="userFactory"
interface="hivemind.example.article.domain.factory.UserFactory"/>
id="userRepository"
interface="hivemind.example.article.domain.repository.UserRepository"/>
id="hibernateSession"
interface="org.hibernate.Session"/>
id="hibernateSessionFactory"
interface="org.apache.hivemind.ServiceImplementationFactory"/>


class="hivemind.example.article.service.impl.RegistrationServiceImpl"/>









class="hivemind.example.article.hibernate.HibernateSessionFactory"
initialize-method="init"/>



service-id="hibernateSessionFactory"
model="threaded"/>



Notice that we also specified the service id of our previously-defined Hibernate
Session factory. Also, Since Hibernate Session objects are lightweight and
not designed to be re-used we chose to use the threaded service
lifecycle model as opposed to the pooled service lifecycle model.
We have omitted a key component from our implementation, transaction control
- but we dont want to insert transaction control logic into our code.
We would rather treat transactions as a cross-cutting concern to
borrow a term from the aspect-oriented programming (AOP). We can do so using
service interceptors.


Service Interceptors


One of the most fundamental forms of AOP is around advice where
logic is inserted before and after a method call. HiveMind supports this concept
using service interceptors. Implementing your own service interceptors can
be quite involved, as it requires you to dynamically generate entirely new
classes at runtime which implement the service interfaces you wish to intercept
(currently done using Javassist or JDK proxies). However, with HiveMind 1.1,
support has been added for the MethodInterceptor interface from the AOP Alliance
project which simplifies the process tremendously (it also opens the door for
reusing outside codesay, the large code base that already exists inside
the Spring framework). We could write our transaction interceptor as follows:



public class TransactionInterceptor implements MethodInterceptor
{
private TransactionService transactionService;

public Object invoke( MethodInvocation methodInvocation ) throws Throwable
{
if( transactionService.isActive() )
{
return proceedWithInvocation( methodInvocation );
}
else
{
try
{
transactionService.begin();
return proceedWithInvocation( methodInvocation );
}
finally
{
if( transactionService.isRollbackOnly() )
{
transactionService.rollback();
}
else
{
transactionService.commit();
}
}
}
}

private Object proceedWithInvocation( MethodInvocation methodInvocation )
throws Throwable
{
try
{
return methodInvocation.proceed();
}
catch( RuntimeException e )
{
transactionService.setRollbackOnly();
throw e;
}
}

public void setTransactionService( TransactionService transactionService )
{
this.transactionService = transactionService;
}
}

This interceptor depends upon a TransactionService object to provide the actual
transaction support. Implementing a TransactionService using Hibernate is quite
easy, so I will not include the code here. Abstracting the transactional logic
this way allows us to easily switch to another technology in the future. Our
transaction interceptor logic would not have to change at all. Also note that
it automatically marks the transaction for rollback upon any RuntimeException
(a la EJB). With this final piece to the puzzle in place, our module descriptor
now looks like:




id="registrationService"
interface="hivemind.example.article.service.RegistrationService"/>
id="emailService"
interface="hivemind.example.article.service.EmailService"/>
id="userFactory"
interface="hivemind.example.article.domain.factory.UserFactory"/>
id="userRepository"
interface="hivemind.example.article.domain.repository.UserRepository"/>
id="hibernateSession"
interface="org.hibernate.Session"/>
id="hibernateSessionFactory"
interface="org.apache.hivemind.ServiceImplementationFactory"/>
id="transactionService"
interface="hivemind.example.article.service.TransactionService"/>
id="transactionInterceptor"
interface="org.aopalliance.intercept.MethodInterceptor"/>





service-id="hivemind.lib.MethodInterceptorFactory"
name="transaction">










class="hivemind.example.article.hibernate.HibernateSessionFactory"
initialize-method="init"/>



service-id="hibernateSessionFactory"
model="threaded"/>


model="threaded">










Notice the insertion of the element inside our element
for the registrationService. This instructs HiveMind to add a
service interceptor to the service using the MethodInterceptorFactory which
is included with HiveMind. Also note the use of the service:transactionInterceptor syntax.
Here, were using the built-in service object provider (object
providers are outside the scope of this article) to tell HiveMind that it is
to use the transactionInterceptor service object for the implementation
of our service interceptor. Now that we have our module completely fleshed
out, we can write a simple application which uses our registration service.


Running the Example


Heres the complete code for our sample application:



public class Main
{
public static void main( String[] args ) throws Exception
{
Registry registry = RegistryBuilder.constructDefaultRegistry();
RegistrationService registrationService =
(RegistrationService)registry.getService(
RegistrationService.class );
try
{
registrationService.registerUser( "user@localhost" );
}
finally
{
registry.cleanupThread();
registry.shutdown();
}
}
}

The first thing we do in our application is create a HiveMind registry object.
A HiveMind registry contains zero or more modules. In this case, all of the
modules in the registry are described by classpath resources named /META-INF/hivemodule.xml (the
default location for a HiveMind module descriptor). This is a very important
and powerful feature! With this one line of code, HiveMind has automatically
discovered and parsed every module descriptor on the classpath and loaded the
corresponding modules into the registry. So, if this were a web application
having one hundred jar files in the /WEB-INF/lib directory, containing
module descriptors, there would be one hundred modules in the resulting registry.
Again, this was done using only one line of code. Because this mechanism leverages
the Java class loader, it is very flexible. If you repackage your web application
with additional libraries containing HiveMind module descriptors, they will
simply be integrated into the Registry the next time the application is started,
without changing any existing code. As you can imagine, this makes assembling
HiveMind-based applications very easy. If need be, a RegistryBuilder instance
can be used to create custom (non-default) registries. In HiveMind 1.1, theres
even a way to use Groovy scripts to define your modules! However, for the most
part, only the default registry will be needed. Next, we used the registry
to lookup our service object for the registration service. HiveMind requires
that you provide the interface you are expecting so that it can ensure youre
going to get what you ask for. From that point on, we can invoke any of the
service objects methods as usual. Thats all there is to it! With
those few lines of code, all of the dependencies were tied together for us
and our services were ready to use.


Conclusion


We have perused many of the key features of HiveMind in some detail. In some
respects, though, we have barely scratched the surface of what HiveMind can
do. For instance, as you experiment with HiveMind, youre bound to make
some mistakes in your module descriptor documents (I made plenty while writing
this article). HiveMind, fortunately, contains a feature known as line-precise
error reporting which can help you quickly identify your mistake. HiveMind
also contains build-in support for localization. Another notable feature not
covered here involves even listener registrations. HiveMind can automatically inject implementation
objects (not services) as event listeners into other services. Perhaps the
biggest omission is a discussion about configuration points, which allow you
to dynamically define XML syntax for passing configuration information into
your services.


HiveMind boasts a thriving community of interested users and developers alike.
At this moment, the development team is diligently working on the 1.1-alpha-3
release, with a full 1.1 release anticipated soon. With so much involvement,
hopefully many more projects like HiveTranse, a SourceForge project aimed at
providing a standardized transaction framework for HiveMind, will start to
emerge. What is needed is for many projects to adopt HiveMind and create reusable
modules and frameworks that others can plug in to their applications. That
is the vision of HiveMind, assembling large applications from small, testable,
reusable components.


Download Hivemind
Sources


About the Author


James Carman an independent software consultant from Cincinnati, OH. He is
one of the directors of our local Cincinnati Java Users Group (www.cinjug.org).
As of late, he has been working in Bioinformatics, but in the past has done
more business-oriented programming. He is also a committer on the Jakarta Commons
and Jakarta HiveMind projects at The Apache Software Foundation.



Posted by 아름프로


The Hidden Gems of Jakarta Commons, Part 1


by Timothy M. O'Brien

12/22/2004



If you are not familiar with the href="http://jakarta.apache.org/commons">Jakarta Commons, you have
likely reinvented a few wheels. Before you write any more generic
frameworks or utilities, grok the Commons. It will save you serious
time. Too many people write a StringUtils class that
duplicates methods available in href="http://jakarta.apache.org/commons/lang">Commons Lang's
StringUtils, or developers unknowingly recreate the
utilities in href="http://jakarta.apache.org/commons/collections">Commons
Collections even though commons-collections.jar is
already available in the classpath. Seriously, take a break. Check
out the Commons Collections API and then go back to your task; I
promise you'll find something simple that will save you a week over
the next year. If people just took some time to look at Jakarta
Commons, we would have much less code duplication--we'd start making
good on the real promise of reuse. I've seen it happen; somebody digs
into Commons BeanUtils or Commons Collections and invariably they have
a "Oh, if I had only known about this, I wouldn't have written 10,000
lines of code" moment. There are still parts of Jakarta Commons that
remain a mystery to most; for instance, many have yet to hear of href="http://jakarta.apache.org/commons/cli">Commons CLI or href="http://jakarta.apache.org/commons/configuration">Commons
Configuration, and most have yet to notice the valuable
functors package in Commons Collections. In this series,
I emphasize some of the less-appreciated tools and utilities in the
Jakarta Commons.




In this first part of the series, I explore XML rule set definitions in
the Commons
Digester
, functors available in Commons Collections, and an
interesting application, href="http://jakarta.apache.org/commons/jxpath">Commons JXPath, to
query a List of objects. Jakarta Commons
contains utilities that aim to help you solve problems at the lowest
level of programming: iterating over collections, parsing XML, and
selecting objects from a List. I would encourage you to
spend some time focusing on these small utilities, as learning about
the Jakarta Commons will save you a substantial amount of time. It
isn't simply about using Commons Digester to parse XML or using
CollectionUtils to filter a collection with a
Predicate. You will start to see benefits once you
realize how to combine the power of these utilities and how to relate
Commons projects to your own applications; once this happens, you will
come to see commons-lang.jar,
commons-beanutils.jar, and
commons-digester.jar as just as indispensable to any system as
the JVM itself.







Related Reading



Jakarta Commons Cookbook

Jakarta Commons Cookbook


By Timothy M.쟏'Brien




Table of Contents

Index

Sample Chapter





Read Online--Safari
Search this book on Safari:





 



Code Fragments only







If you are interested in learning more about the Jakarta Commons,
check out the Jakarta Commons
Cookbook
. This book is full of recipes that will get you hooked
on the Commons, and tells you how to use Jakarta Commons in concert
with other small open source components such as href="http://jakarta.apache.org/velocity">Velocity, href="http://www.freemarker.org">FreeMarker, href="http://jakarta.apache.org/lucene">Lucene, and href="http://jakarta.apache.org/slide">Jakarta Slide. In this
book, I introduce a wide array of tools from Jakarta Commons from
using simple utilities in Commons Lang to combining Commons Digester,
Commons Collections, and Jakarta Lucene to search the works of William
Shakespeare. I hope this series and the href="http://www.oreilly.com//catalog/jakartackbk">Jakarta Commons Cookbook provide you
with some interesting solutions for low-level programming problems.



1. XML-Based Rule Sets for Commons Digester




Commons Digester
1.6
provides one of the easiest ways to turn XML into objects.
Digester has already been introduced on the O'Reilly network in two
articles: "Learning
and Using Jakarta Digester
," by Philipp K. Janert, and " href="http://www.oreillynet.com/pub/a/onjava/2003/07/09/commons.html"/>Using the Jakarta
Commons, Part 2," by Vikram Goyal. Both articles demonstrate the use of
XML rule sets, but this idea of defining rule sets in XML has not
caught on. Most sightings of the Digester appear to define rule sets
programmatically, in compiled code. You should avoid hard-coding
Digester rule sets in compiled Java code when you have the opportunity
to store such mapping information in an external file or a classpath
resource. Externalizing a Digester rule set makes it easier to adapt to an
evolving XML document structure or an evolving object model.




To demonstrate the difference between defining rule sets in XML and
defining rule sets in compiled code, consider a system to parse XML to
a Person bean with three properties--id,
name, and age, as defined in the following class:



package org.test;

public class Person {
public String id;
public String name;
public int age;

public Person() {}

public String getId() { return id; }
public void setId(String id) {
this.id = id;
}

public String getName() { return name; }
public void setName(String name) {
this.name = name;
}

public int getAge() { return age; }
public void setAge(int age) {
this.age = age;
}
}



Assume that your application needs to parse an XML file containing
multiple person elements. The following XML file,
data.xml, contains two person elements
that you would like to parse into Person objects:



<people>
<person id="1">
<name>Tom Higgins</name>
<age>25</age>
</person>
<person id="2">
<name>Barney Smith</name>
<age>75</age>
</person>
<person id="3">
<name>Susan Shields</name>
<age>53</age>
</person>
</people>



You expect the structure and content of this XML file to change over
the next few months, and you would prefer not to hard-code the
structure of the XML document in compiled Java code. To do this, you
need to define Digester rules in an XML file that is loaded as a
resource from the classpath. The following XML document,
person-rules.xml, maps the person element to
the Person bean:



<digester-rules>
<pattern value="people/person">
<object-create-rule classname="org.test.Person"/>
<set-next-rule methodname="add"
paramtype="java.lang.Object"/>
<set-properties-rule/>
<bean-property-setter-rule pattern="name"/>
<bean-property-setter-rule pattern="age"/>
</pattern>
</digester-rules>



All this does is instruct the Digester to create a new instance of
Person every time it encounters a person
element, call add() to add this Person to an
ArrayList, set any bean properties that match attributes
on the person element, and set the name and
age properties from the sub-elements name
and age. You've seen the Person class, the
XML document to be parsed, and the Digester rule definitions in XML
form. Now you need to create an instance of Digester with
the rules defined in person-rules.xml. The following
code creates a Digester by passing the URL
of the person-rules.xml resource to the
DigesterLoader. Since the person-rules.xml
file is a classpath resource in the same package as the class parsing
the XML, the URL is obtained with a call to
getClass().getResource(). The
DigesterLoader then parses the rule definitions and adds
these rules to the newly created Digester:



import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;

// Configure Digester from XML ruleset
URL rules = getClass().getResource("./person-rules.xml");
Digester digester =
DigesterLoader.createDigester(rules);

// Push empty List onto Digester's Stack
List people = new ArrayList();
digester.push( people );

// Parse the XML document
InputStream input = new FileInputStream( "data.xml" );
digester.parse( input );



Once the Digester has parsed the XML in
data.xml, three Person objects should be in
the people ArrayList.




The alternative to defining Digester rules in XML is to add them using
the convenience methods on a Digester instance. Most
articles and examples start with this method, adding rules using the
addObjectCreate() and
addBeanPropertySetter() methods on Digester.
The following code adds the same rules that were defined in
person-rules.xml:



digester.addObjectCreate("people/person", 
Person.class);
digester.addSetNext("people/person",
"add",
"java.lang.Object");
digester.addBeanPropertySetter("people/person",
"name");
digester.addBeanPropertySetter("people/person",
"age");



If you have ever found yourself working at an organization with
2500-line classes to parse a huge XML document with SAX, or a whole
collection of classes to work with DOM or JDOM, you understand that
XML parsing is more complex than it needs to be, in the majority of
cases
. If you are building a highly efficient system with strict
speed and memory requirements, you need the speed of a SAX parser. If
you need the complexity of the DOM Level 3, use a parser like href="http://xml.apache.org/#xerces">Apache Xerces. But if you
are simply trying to parse a few XML documents into objects, take a
look at Commons Digester, and define your rule set in an XML file.




Any time you can move this type of configuration outside of compiled
code, you should. I would encourage you to define your digester rules
in an XML file loaded either from the file system or the classpath.
Doing so will make it easier to adapt your program to changes in the
XML document and changes in your object model. For more information
on defining Digester rules in an XML file, see Section 6.2 of the href="http://www.oreilly.com/catalog/jakartackbk">Jakarta Commons Cookbook, "Turning
XML Documents into Objects."
























2. Functors in Commons Collections




Functors are an interesting part of Commons Collections 3.1 for two
reasons: they haven't received the attention they warrant, and they
have the potential to change the way you approach programming.
Functor is just a fancy name for an object that
encapsulates a function--a "functional object." And
while they are certainly not the same thing, if you have ever used
method pointers in C or C++, you'll understand the power of functors.
A functor is an object--a Predicate, a
Closure, or a
Transformer. Predicates evaluate objects and
return a boolean, Transformers evaluate objects and
return new objects, and Closures accept objects and
execute code. Functors can be combined into composite functors that
model loops, logical expressions, and control structures, and functors
can also be used to filter and operate upon items in a collection.




Explaining functors in an article as short as this may be impossible,
so to "jump start" your introduction to functors, I will solve the same problem both with and without functors.
In this example, Student objects from an
ArrayList are sorted into two List instances
if they meet certain criteria; students with straight-A grades are
added to an honorRollStudents list, and students with Ds
and Fs are added to a problemStudents list. After the
students are separated, the system will iterate through each list,
giving the honor-roll students an award and scheduling a meeting with
parents of problem students. The following code implements this
process without the use of functors
:



List allStudents = getAllStudents();

// Create 2 ArrayLists to hold honorRoll students
// and problem students
List honorRollStudents = new ArrayList();
List problemStudents = new ArrayList();

// Iterate through all students. Put the
// honorRoll students in one List and the
// problem students in another.
Iterator allStudentsIter = allStudents.iterator();
while( allStudentsIter.hasNext() ) {
Student s = (Student) allStudentsIter.next();

if( s.getGrade().equals( "A" ) ) {
honorRollStudents.add( s );
} else if( s.getGrade().equals( "B" ) &&
s.getAttendance() == PERFECT) {
honorRollStudents.add( s );
} else if( s.getGrade().equals( "D" ) ||
s.getGrade().equals( "F" ) ) {
problemStudents.add( s );
} else if( s.getStatus() == SUSPENDED ) {
problemStudents.add( s );
}
}

// For all honorRoll students, add an award and
// save to the Database.
Iterator honorRollIter =
honorRollStudents.iterator();
while( honorRollIter.hasNext() ) {
Student s = (Student) honorRollIter.next();

// Add an award to student record
s.addAward( "honor roll", 2005 );
Database.saveStudent( s );
}

// For all problem students, add a note and
// save to the database.
Iterator problemIter = problemStudents.iterator();
while( problemIter.hasNext() ) {
Student s = (Student) problemIter.next();

// Flag student for special attention
s.addNote( "talk to student", 2005 );
s.addNote( "meeting with parents", 2005 );
Database.saveStudent( s );
}



The previous example is very procedural; the only way to figure out
what happens to a Student object is to step through each
line of code. The first half of this example is decision logic that
applies tests to each Student object and classifies
students based on performance and attendance. The second half of this
example operates on the Student objects and saves the result to the
database. A 50-line method body like the previous example is how most
systems begin--manageable procedural complexity. But problems start
to appear when the requirements start to shift. As soon as that
decision logic changes, you will need to start adding more clauses to
the logical expressions in the first half of the previous example.
For example, what happens to your logical expression if a student is
classified as a problem if he has a B and perfect attendance, but
attended detention more than five times? Or what happens to the
second half, when a student can be on the honor roll only if they were
not a problem last year? When exceptions and requirement changes
start to affect procedural code, manageable complexity turns into
unmaintainable spaghetti code.




Step back from the previous example and consider what that code was
doing. It was looking at every object in a List,
applying a criteria, and, if that criteria was satisfied, acting upon
an object. A critical improvement that could be made to the previous
example is the decoupling of the criteria from the code that acts upon
an object. The following two code excerpts solve the previous problem
in a very different way. First, the criteria for the honor roll and
problem students are modeled by two Predicate objects,
and the code that acts upon honor roll and problem students is
modeled by two Closure objects. These four objects are
defined below:



import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
public boolean evaluate(Object object) {
Student s = (Student) object;

return( ( s.getGrade().equals( "A" ) ) ||
( s.getGrade().equals( "B" ) &&
s.getAttendance() == PERFECT ) );
}
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
public boolean evaluate(Object object) {
Student s = (Student) object;

return ( ( s.getGrade().equals( "D" ) ||
s.getGrade().equals( "F" ) ) ||
s.getStatus() == SUSPENDED );
}
};

// Anonymous Closure that adds a student to the
// honor roll
Closure addToHonorRoll = new Closure() {
public void execute(Object object) {
Student s = (Student) object;

// Add an award to student record
s.addAward( "honor roll", 2005 );
Database.saveStudent( s );
}
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
public void execute(Object object) {
Student s = (Student) object;

// Flag student for special attention
s.addNote( "talk to student", 2005 );
s.addNote( "meeting with parents", 2005 );
Database.saveStudent( s );
}
};



The four anonymous implementations of Predicate and
Closure are separated from the system as a whole.
flagForAttention has no knowledge of what the criteria
are for a problem student, and the isProblem Predicate
only knows how to identify a problem student. What is needed is a way
to marry the right Predicate with the right
Closure, and this is shown in the following example.



import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents =
ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );



In the previous code, the predicateMap matches
Predicates to Closures; if a
Student satisfies the Predicate in the key,
it will be passed to the Closure in the value. By
supplying a NOPClosure value and a null key,
we will pass Student objects that satisfy neither
Predicate to a "do nothing" or "no operation"
NOPClosure created by a call to
ClosureUtils. A SwitchClosure,
processStudents, is created from the
predicateMap, and the processStudents
Closure is applied to every Student object
in the allStudents using
CollectionUtils.forAllDo(). This is a very different
approach; notice that you are not iterating through any lists.
Instead, you set rules and consequences and
CollectionUtils and SwitchClosure take care
of the execution.





When you separate criteria using Predicates and actions
using Closures, your code is less procedural and much
easier to test. The isHonorRoll Predicate can be unit
tested in isolation from the addToHonorRoll Closure, and
both can be tested by supplying a mock instance of the
Student class. The second example also demonstrates
CollectionUtils.forAllDo(), which applies a
Closure to every element in a Collection.
You may have noticed that using functors did not reduce the line count; in
fact, the use of functors increased the line count. But the real benefit
from functors is the modularity and encapsulation of criteria and
actions. If your method length tends towards hundreds of lines,
consider an less procedural, more object-oriented approach--use a
functor.




Chapter 4, "Functors," in the Jakarta Commons
Cookbook
introduces functors available in Commons Collections, and
Chapter 5, "Collections," shows you how to use functors with the Java Collections
API. All of the functors--Closure,
Predicate, and Transformer--can be combined
into composite functors that can be used to model any kind of logic.
switch, while, and for
structures can be modeled with SwitchClosure,
WhileClosure, and ForClosure. Compound
logical expressions can be constructed from multiple
Predicates using OrPredicate,
AndPredicate, AllPredicate, and
NonePredicate, among others. Commons BeanUtils also
contains functor implementations that are used to apply functors to
bean properties--BeanPredicate,
BeanComparator, and
BeanPropertyValueChangeClosure. Functors are a different
way of thinking about low-level application architecture, and they
could very well change your approach to coding.



3. Using XPath Syntax to Query Objects and Collections




Commons JXPath
is a surprising (non-standard) use of an XML standard. XPath has been
around for some time as a way to select a node or node set in an XSL
style sheet. If you've worked with XML, you are probably familiar with
the syntax /foo/bar that selects the bar
sub-elements of the foo document element. Jakarta Commons
JXPath adds an interesting twist: you can use JXPath to select objects
from beans and collections, among other object types such as servlet
contexts and DOM Document objects. Consider a
List of Person objects. Each
Person object has a bean property of the type
Job, and each Job object has a
salary property of the type int.
Person objects also have a country property,
which is a two-letter country code. Using JXPath, it is easy to
select all Person objects with a US country
and a Job that pays more than one million
dollars. Here is some code to set up a List of beans to
filter with JXPath:



// Person's constructor sets firstName and country
Person person1 = new Person( "Tim", "US" );
Person person2 = new Person( "John", "US" );
Person person3 = new Person( "Al", "US" );
Person person4 = new Person( "Tony", "GB" );

// Job's constructor sets name and salary
person1.setJob( new Job( "Developer", 40000 ) );
person2.setJob( new Job( "Senator", 150000 ) );
person3.setJob( new Job( "Comedian", 3400302 ) );
person4.setJob( new Job( "Minister", 2000000 ) );

Person[] personArr =
new Person[] { person1, person2,
person3, person4 };

List people = Arrays.asList( personArr );



The people List contains four
Person beans: Tim, John, Al, and George. Tim is a
developer who makes $40,000, John is a Senator who makes $150,000, Al
is a comedian who walks home with $3.4 million, and Tony is a prime
minister who makes 2 million euros. Our task is simple: iterate over
this List and print the name of every Person
who is a U.S. citizen making over one million dollars. Assume that
people is an ArrayList of
Person objects, and take a look at the solution without
the benefit of JXPath:



Iterator peopleIter = people.getIterator();
while( peopleIter.hasNext() ) {
Person person = (Person) peopleIter.next();

if( person.getCountry() != null &&
person.getCountry().equals( "US" ) &&
person.getJob() != null &&
person.getJob().getSalary() > 1000000 ) {
print( person.getFirstName() + " "
person.getLastName() );
}
}
}
}



The previous example is heavy, and somewhat error-prone. To find the
matching Person objects, you first need to iterate over
each Person and test the country property of
each. If the country property is not null
and it has the correct value, then you must test the job
property to find out if it is non-null and has
salary property greater than 1000000. The line count of
the previous example can be dramatically reduced with Java 1.5's
for syntax, but, even with Java 1.5, you still need to
perform two comparisons at two different levels.




What if you had to write a number of these queries against a set of
Person objects stored in memory? What if your
application had to display all of the Person objects in
England named Tony? Or, what if you had to print the name
of every Job with a salary less than 20,000? If you were
storing these objects in a relational database, you could solve this
by writing a SQL query, but if you are dealing with objects in memory,
you don't have this luxury. While XPath was primarily meant for XML,
you could use it to write "queries" against a collection of objects,
treating objects as elements and bean properties as sub-elements.
Yes, this is a strange application of XPath, but take a look at how
the following example performs three different queries against
people, an ArrayList of Person
objects.



import org.apache.commons.jxpath.JXPathContext;

public List queryCollection(String xpath,
Collection col) {
List results = new ArrayList();

JXPathContext context =
JXPathContext.newContext( col );

Iterator matching =
context.iterate( xpath );

while( matching.hasNext() ) {
results.add( matching.getNext() );
}
return results;
}

String query1 =
".[@country = 'US']/job[@salary > 1000000]/..";
String query2 =
".[@country = 'GB' and @name = 'Tony']";
String query3 =
"./job/name";

List richUsPeople =
queryCollection( query1, people );
List britishTony =
queryCollection( query2, people );
List jobNames =
queryCollection( query3, people );



The method queryCollection() takes an XPath expression
and applies it to a Collection. XPath expressions are
evaluated against a JXPathContext, which is created by
calling JXPathContext.newContext() and passing in the
Collection to be queried. Calling
context.iterate() then applies the XPath expression to
each item in the Collection, returning an
Iterator with every matching "node" (or in this
case, "object"). The first query performed by the previous
example, query1, is same query from the original example
implemented without JXPath. query2 selects all
Person objects with a country property of
GB and a name property of Tony,
and query3 selects a List of String
objects, the name property of all of the Job
objects.




When I first saw Commons JXPath, it struck me as a bad idea. Why apply
XPath expressions to objects? Something about it didn't feel right.
But this unexpected use of XPath as a query language for a collection
of beans has come in handy for me more than a few times in the past few
years. If you find yourself looping through lists to find matching
elements, consider using JXPath. For more information, see Chapter 12,
"Searching and Filter," of Jakarta
Commons Cookbook
, which discusses Commons JXPath and Jakarta Lucene
paired with Commons Digester.



And There's More




Stay tuned to this exploration of the far reaches of the Jakarta
Commons. In the next part of this series, I'll introduce some related
tools and utilities. Set operations in Commons Collections, using
Predicate objects with collections, configuring an application with href="http://jakarta.apache.org/commons/configuration">Commons
Configuration, and using href="http://jakarta.apache.org/commons/betwixt">Commons Betwixt
to read and write XML. There is much to be gained from the Jakarta
Commons that cannot be conveyed in a few thousand words, and I would
encourage you to take a look at the href="http://www.oreilly.com/catalog/jakartackbk">Jakarta Commons Cookbook. Many of
these utilities may, at first glance, seem somewhat trivial, but the
power of Jakarta Commons lies in how these tools can be combined with
each other and integrated into your own systems.




Timothy M. O'Brien
is a professional singer/programmer living and working in the Chicago area.




Posted by 아름프로
아래와 같이...
별도의 bat 파일을 작성한다.. ㅡㅡ;;
tomcat 버젼별로 개발할 시 도움..
===========================

set CATALINA_HOME=../
call ./startup.bat
Posted by 아름프로
struts의 ApplicationResource를 사용함에 있어서.. 문제가 좀 되기는하지만,
일단 기본 환경설정상으로의 한글처리 방법을 적어봅니다.

1. velocity.properties 파일에.. 아래 내용 추가
input.encoding = EUC-KR
output.encoding = EUC-KR

2. tomcat의 server.xml 에서 connect  부분에.. URIEncoding 추가
<Connector port="8080"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               debug="0" connectionTimeout="20000"
               disableUploadTimeout="true"  URIEncoding="EUC-KR" />

=========================================
1만 했으시에는 화면상의 한글은 잘되는데 파라메터 처리에서 한글 깨짐

Posted by 아름프로
사이트의 Velocity Tools을 처음 접하시는 분은
Generic Tools / VelocityView Tools / VelocityStruts
3개의 큰 카테고리(!)와 그 안에 세분화 된 Tool이 있다는 것을
어렵게 이해할 수 있답니다.
한눈으로 파악하기 좋게 옮겨봤습니다.


================================================
Generic Tools
--------------------------------------------------------------------------------------
DateTool :
A tool for manipulating and formatting dates.

MathTool :
A tool for performing floating point math.

NumberTool :
A tool for formatting numbers.

IteratorTool :
A convenience tool to use with #foreach loops. It wraps a list to let the designer specify a condition to terminate the loop, and reuse the same list in different loops.

RenderTool :
A tool to evaluate and render arbitrary strings of VTL (Velocity Template Language).

==================================================
VelocityView Tools
--------------------------------------------------------------------------------------
AbstractSearchTool :
Abstract view tool for doing "searching" and robust pagination of search results.

CookieTool :
View tool for convenient cookie access and creation.

LinkTool :
The LinkTool provides methods to work with URIs:

ParameterParser :
View tool for easy parsing of ServletRequest parameters.

ViewRenderTool :
This tool expose methods to evaluate the given strings as VTL (Velocity Template Language) and automatically using the current context

==================================================
VelocityStruts
--------------------------------------------------------------------------------------
ActionMessagesTool :
View tool to work with the Struts action messages.

ErrorsTool :
This tool deals with Struts error messages.

FormTool :
Struts has support to parse incoming HTTP requests and populate a Java bean with the submitted request

MessageTool :
The MessageTool is used to render internationalized message strings.

SecureLinkTool :
Tool to be able to use Struts SSL Extensions with Velocity

StrutsLinkTool :
The StrutsLinkTool extends the standard LinkTool to add methods for working with Struts' Actions and Forwards

TilesTool :
View tool to use struts-tiles with Velocity

ValidatorTool :
View tool that works with Struts Validator to produce client side javascript validation for your forms.

=============================================
Posted by 아름프로
html 상에서 아래와 같이 화면단위의 처리를 ...
<meta http-equiv="Content-Type" content="text/html; charset=euc-kr">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Pragma" content="no-cache">

struts-config.xml 에서의

<controller
  contentType="text/html;charset=euc-kr"
  nocache="true" />

설정만 해주면 된다.






***** 아름다운프로님에 의해서 게시물 복사 + 카테고리유지되었습니다 (2003-12-18 17:23)
Posted by 아름프로
차니랍니다.
이것 때문에 한참 바보 짓 했습니다.

4.1.24 버젼에서는 괜찮았던 녀석이 27로 업하면 안되고..
4.1.29 는 eclipse tomcat plugin 현재까지의 버젼으로는 안돌아가고.. ㅡㅡ;;

어쨌뜬.. 내용즉,

4.1.27에 버그였답니다.

버그 패치 파일은..

http://archive.apache.org/dist/jakarta/tomcat-4/binaries/

여기에서

4.1.27-hotfix-22096.zip  

이 녀석 받아서 설치하면 됩니다.
디렉토리는 풀어보시면 바로 압니다.

쩝..
(헛짓꺼리로 2시간 날림.. ㅡㅡ;)




***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:36)
Posted by 아름프로

web.xml 에서 param-value 의 값을 false로 수정하면 된다.

       <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>




***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:36)
Posted by 아름프로
Struts TagLibs and JSPs
좋네요~~




***** 아름다운프로님에 의해서 게시물 복사 + 카테고리유지되었습니다 (2003-12-18 17:23)
Posted by 아름프로
데모
http://www.cardiff.com/techdocu/T1161/T1161.htm

OnJava의 SSO Tip
http://www.onjava.com/pub/a/onjava/2003/06/25/tomcat_tips.html?page=last&x-showcontent=text

jaffa
http://jaffa.sourceforge.net/documentation/security/web/#single



***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:36)
Posted by 아름프로
Coauthor's note: Now that writing Java web applications has become a common way to create and deploy new web content, people around the globe are finding the Jakarta Tomcat servlet and JSP container useful. It's free, it's multiplatform, it's rich in features, it's rapidly evolving and improving, and it's never been more popular.

The only catch seems to be this: how can you configure Tomcat to do what you want it to do? Tomcat is capable, as long as you can configure it to suit your needs. Below is my list of ten Tomcat configuration tips, taken from Tomcat: The Definitive Guide, to help you do just that. -- Jason Brittain

1. Configuring the Admin Web Application
Most commercial J2EE servers provide a fully functional administrative interface, and many of these are accessible as web applications. The Tomcat Admin application is on its way to becoming a full-blown Tomcat administration tool rivaling these commercial offerings. First included in Tomcat 4.1, Admin already provides control over contexts, data sources, and users and groups. You can also control resources such as initialization parameters, as well as users, groups, and roles in a variety of user databases. The list of capabilities will be expanded upon in future releases, but the present implementation has proven itself to be quite useful.

Related Reading


Tomcat: The Definitive Guide
By Jason Brittain, Ian F. Darwin

Table of Contents
Index
Sample Chapter

Read Online--Safari Search this book on Safari:
    
Only This Book All of Safari
Code Fragments only  
The Admin web application is defined in the auto-deployment file CATALINA_BASE/webapps/admin.xml.

You must edit this file to ensure that the path specified in the docBase attribute of the Context element is absolute; that is, the absolute path of CATALINA_HOME/server/webapps/admin. Alternatively, you could just remove the auto-deployment file and specify the Admin context manually in your server.xml file. On machines that will not be managed by this application, you should probably disable it altogether by simply removing CATALINA_BASE/webapps/admin.xml.

If you're using a UserDatabaseRealm (the default), you'll need to add a user and a role to the CATALINA_BASE/conf/tomcat-users.xml file. For now, just edit this file, and add a role named "admin" to your users database:

<role name="admin"/>
You must also have a user who is assigned to the "admin" role. Add a user line like this after the existing user entries (changing the password to something a bit more secure):

<user name="admin" password="deep_dark_secret" roles="admin"/>
Once you've performed these steps and restarted Tomcat, visit the URL http://localhost:8080/admin, and you should see a login screen. The Admin application is built using container-managed security and the Jakarta Struts framework. Once you have logged in as a user assigned to the admin role, you will be able to use the Admin application to configure Tomcat.

2. Configuring the Manager Web Application
The Manager web application lets you perform simple management tasks on your web applications through a more simplified web user interface than that of the Admin web app.

The Manager web application is defined in the auto-deployment file CATALINA_BASE/webapps/manager.xml.

You must edit this file to ensure that the path specified in the docBase attribute of the Context element is absolute; that is, the absolute path of CATALINA_HOME/server/webapps/manager.

If you're using the default UserDatabaseRealm, you'll need to add a user and role to the CATALINA_BASE/conf/tomcat-users.xml file. For now, just edit this file, and add a role named "manager" to your users database:

<role name="manager"/>
You must also have a user who is assigned the "manager" role. Add a user line like this after the existing user entries (changing the password to something a bit more secure):

<user name="manager" password="deep_dark_secret" roles="manager"/>
Then restart Tomcat and visit the URL http://localhost/manager/list to see the plain-text manager interface, or http://localhost/manager/html/list for the simple HTML manager interface. Either way, your Manager application should now be working.

The Manager application lets you install new web applications on a non-persistent basis, for testing. If we have a web application in /home/user/hello and want to test it by installing it under the URI /hello, we put "/hello" in the first text input field (for Path) and "file:/home/user/hello" in the second text input field (for Config URL).

The Manager also allows you to stop, reload, remove, or undeploy a web application. Stopping an application makes it unavailable until further notice, but of course it can then be restarted. Users attempting to access a stopped application will receive an error message, such as 503 - This application is not currently available.

Removing a web application removes it only from the running copy of Tomcat -- if it was started from the configuration files, it will reappear the next time you restart Tomcat (i.e., removal does not remove the web application's content from disk).

3. Deploying a Web Application
There are two ways of deploying a web application on the filesystem:

1. Copy your WAR file or your web application's directory (including all of its content) to the $CATALINA_BASE/webapps directory.

2. Create an XML fragment file with just the Context element for your web application, and place this XML file in $CATALINA_BASE/webapps. The web application itself can then be stored anywhere on your filesystem.

If you have a WAR file, you can deploy it by simply copying the WAR file into the directory CATALINA_BASE/webapps. The filename must end with an extension of ".war". Once Tomcat notices the file, it will (by default) unpack it into a subdirectory with the base name of the WAR file. It will then create a context in memory, just as though you had created one by editing Tomcat's server.xml file. However, any necessary defaults will be obtained from the DefaultContext element in Tomcat's server.xml file.

Another way to deploy a web app is by writing a Context XML fragment file and deploying it into the CATALINA_BASE/webapps directory. A context fragment is not a complete XML document, but just one Context element and any subelements that are appropriate for your web application. These files are like Context elements cut out of the server.xml file, hence the name "context fragment."

For example, if we wanted to deploy the WAR file MyWebApp.war along with a realm for accessing parts of that web application, we could use this fragment:


<!--  
  Context fragment for deploying MyWebApp.war  
-->
<Context path="/demo" docBase="webapps/MyWebApp.war"
         debug="0" privileged="true">
  <Realm className="org.apache.catalina.realm.UserDatabaseRealm"                
         resourceName="UserDatabase"/>
</Context>

Put that in a file called "MyWebApp.xml," and copy it into your CATALINA_BASE/webapps directory.

These context fragments provide a convenient method of deploying web applications; you do not need to edit the server.xml file and, unless you have turned off the default liveDeploy feature, you don't have to restart Tomcat to install a new web application.

4. Configuring Virtual Hosts
The Host element normally needs modification only when you are setting up virtual hosts. Virtual hosting is a mechanism whereby one web server process can serve multiple domain names, giving each domain the appearance of having its own server. In fact, the majority of small business web sites are implemented as virtual hosts, due to the expense of connecting a computer directly to the Internet with sufficient bandwidth to provide reasonable response times and the stability of a permanent IP address.

Name-based virtual hosting is created on any web server by establishing an aliased IP address in the Domain Name Service (DNS) data and telling the web server to map all requests destined for the aliased address to a particular directory of web pages. Since this article is about Tomcat, we don't try to show all of the ways to set up DNS data on various operating systems. If you need help with this, please refer to DNS and Bind, by Paul Albitz and Cricket Liu (O'Reilly). For demonstration purposes, I'll use a static hosts file, since that's the easiest way to set up aliases for testing purposes.

To use virtual hosts in Tomcat, you just need to set up the DNS or hosts data for the host. For testing, making an IP alias for localhost is sufficient. You then need to add a few lines to the server.xml configuration file:


<Server port="8005" shutdown="SHUTDOWN" debug="0">
  <Service name="Tomcat-Standalone">
    <Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
                        port="8080" minProcessors="5" maxProcessors="75"
                        enableLookups="true" redirectPort="8443"/>
    <Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
                        port="8443" minProcessors="5" maxProcessors="75"
                        acceptCount="10" debug="0" scheme="https" secure="true"/>
      <Factory className="org.apache.coyote.tomcat4.CoyoteServerSocketFactory"
                        clientAuth="false" protocol="TLS" />
    </Connector>
    <Engine name="Standalone" defaultHost="localhost" debug="0">
      <!-- This Host is the default Host -->
      <Host name="localhost" debug="0" appBase="webapps"
              unpackWARs="true" autoDeploy="true">
        <Context path="" docBase="ROOT" debug="0"/>
        <Context path="/orders" docBase="/home/ian/orders" debug="0"
                       reloadable="true" crossContext="true">
        </Context>
      </Host>

      <!-- This Host is the first "Virtual Host": www.example.com -->
      <Host name="www.example.com" appBase="/home/example/webapp">
        <Context path="" docBase="."/>
      </Host>

    </Engine>
  </Service>
</Server>
Tomcat's server.xml file, as distributed, contains only one virtual host, but it is easy to add support for additional virtual hosts. The simplified version of the server.xml file in the previous example shows in bold the overall additional structure needed to add one virtual host. Each Host element must have one or more Context elements within it; one of these must be the default Context for this host, which is specified by having its relative path set to the empty string (for example, path="").

5. Configuring Basic Authentication
Container-managed authentication methods control how a user's credentials are verified when a web app's protected resource is accessed. When a web application uses basic authentication (BASIC in the web.xml file's auth-method element), Tomcat uses HTTP basic authentication to ask the web browser for a username and password whenever the browser requests a resource of that protected web application. With this authentication method, all passwords are sent across the network in base64-encoded text.

Note: using basic authentication is generally considered insecure because it does not strongly encrypt passwords, unless the site also uses HTTPS or some other form of encryption between the client and the server (for instance, a virtual private network). Without this extra encryption, network monitors can intercept (and misuse) users' passwords. But, if you're just starting to use Tomcat, or if you just want to test container-managed security with your web app, basic authentication is easy to set up and test. Just add <security-constraint> and <login-config> elements to your web app's web.xml file, and add the appropriate <role> and <user> elements to your CATALINA_BASE/conf/tomcat-users.xml file, restart Tomcat, and Tomcat takes care of the rest.

The example below shows a web.xml excerpt from a club membership web site with a members-only subdirectory that is protected using basic authentication. Note that this effectively takes the place of the Apache web server's .htaccess files.


<!--
  Define the Members-only area, by defining
  a "Security Constraint" on this Application, and
  mapping it to the subdirectory (URL) that we want
  to restrict.
-->
<security-constraint>
  <web-resource-collection>
    <web-resource-name>
      Entire Application
    </web-resource-name>
    <url-pattern>/members/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
      <role-name>member</role-name>
  </auth-constraint>
</security-constraint>
<!-- Define the Login Configuration for this Application -->
<login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>My Club Members-only Area</realm-name>
</login-config>






6. Configuring Single Sign-On
Once you've set up your realm and method of authentication, you'll need to deal with the actual process of logging the user in. More often than not, logging into an application is a nuisance to an end user, and you will need to minimize the number of times they must authenticate. By default, each web application will ask the user to log in the first time the user requests a protected resource. This can seem like a hassle to your users if you run multiple web applications and each application asks the user to authenticate. Users cannot tell how many separate applications make up any single web site, so they won't know when they're making a request that crosses a context boundary, and will wonder why they're being repeatedly asked to log in.

The "single sign-on" feature of Tomcat 4 allows a user to authenticate only once to access all of the web applications loaded under a virtual host. To use this feature, you need only add a SingleSignOn Valve element at the host level. This looks like the following:


<Valve className="org.apache.catalina.authenticator.SingleSignOn"
       debug="0"/>

The Tomcat distribution's default server.xml contains a commented-out single sign-on Valve configuration example that you can uncomment and use. Then, any user who is considered valid in a context within the configured virtual host will be considered valid in all other contexts for that same host.

There are several important restrictions for using the single sign-on valve:

The valve must be configured and nested within the same Host element that the web applications (represented by Context elements) are nested within.

The Realm that contains the shared user information must be configured either at the level of the same Host or in an outer nesting.

The Realm cannot be overridden at the Context level.

The web applications that use single sign-on must use one of Tomcat's built-in authenticators (in the <auth-method> element of web.xml), rather than a custom authenticator. The built-in methods are basic, digest, form, and client-cert authentication.

If you're using single sign-on and wish to integrate another third-party web application into your web site, and the new web application uses only its own authentication code that doesn't use container-managed security, you're basically stuck. Your users will have to log in once for all of the web applications that use single sign-on, and then once again if they make a request to the new third-party web application. Of course, if you get the source and you're a developer, you could fix it, but that's probably not so easy to do.

The single sign-on valve requires the use of HTTP cookies.

7. Configuring Customized User Directories
Some sites like to allow individual users to publish a directory of web pages on the server. For example, a university department might want to give each student a public area, or an ISP might make some web space available on one of its servers to customers that don't have a virtually hosted web server. In such cases, it is typical to use the tilde character (~) plus the user's name as the virtual path of that user's web site:

http://www.cs.myuniversity.edu/~username
http://members.mybigisp.com/~username
Tomcat gives you two ways to map this on a per-host basis, using a couple of special Listener elements. The Listener's className attribute should be org.apache.catalina.startup.UserConfig, with the userClass attribute specifying one of several mapping classes. If your system runs Unix, has a standard /etc/passwd file that is readable by the account running Tomcat, and that file specifies users' home directories, use the PasswdUserDatabase mapping class:

<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html"
userClass="org.apache.catalina.startup.PasswdUserDatabase"/>
Web files would need to be in directories such as /home/users/ian/public_html or /users/jbrittain/public_html. Of course, you can change public_html to be whatever subdirectory into which your users put their personal web pages.

In fact, the directories don't have to be inside of a user's home directory at all. If you don't have a password file but want to map from a user name to a subdirectory of a common parent directory such as /home, use the HomesUserDatabase class:

<Listener className="org.apache.catalina.startup.UserConfig"
directoryName="public_html" homeBase="/home"
userClass="org.apache.catalina.startup.HomesUserDatabase"/>
In this case, web files would be in directories such as /home/ian/public_html or /home/jasonb/public_html. This format is more useful on Windows, where you'd likely use a directory such as C:home.

These Listener elements, if present, must be inside of a Host element, but not inside of a Context element, as they apply to the Host itself.

8. Using CGI Scripts with Tomcat
Tomcat is primarily meant to be a servlet/JSP container, but it has many capabilities rivalling a traditional web server. One of these is support for the Common Gateway Interface (CGI), which provides a means for running an external program in response to a browser request, typically to process a web-based form. CGI is called "common" because it can invoke programs in almost any programming or scripting language: Perl, Python, awk, Unix shell scripting, and even Java are all supported options. However, you probably wouldn't run a Java application as a CGI due to the start-up overhead; elimination of this overhead was what led to the original design of the servlet specification. Servlets are almost always more efficient than CGIs because you're not starting up a new operating-system-level process every time somebody clicks on a link or button.

Tomcat includes an optional CGI servlet that allows you to run legacy CGI scripts; the assumption is that most new back-end processing will be done by user-defined servlets and JSPs.

To enable Tomcat's CGI servlet, you must do the following:

Rename the file servlets-cgi.renametojar (found in CATALINA_HOME/server/lib/) to servlets-cgi.jar, so that the servlet that processes CGI scripts will be on Tomcat's CLASSPATH.

In Tomcat's CATALINA_BASE/conf/web.xml file, uncomment the definition of the servlet named cgi (this is around line 241 in the distribution).

Also in Tomcat's web.xml, uncomment the servlet mapping for the cgi servlet (around line 299 in the distributed file). Remember, this specifies the HTML links to the CGI script.

Either place the CGI scripts under the WEB-INF/cgi directory (remember that WEB-INF is a safe place to hide things that you don't want the user to be able to view, for security reasons), or place them in some other directory within your context and adjust the cgiPathPrefix initialization parameter of the CGIServlet to identify the directory containing the files. This specifies the actual location of the CGI scripts, which typically will not be the same as the URL in the previous step.

Restart Tomcat, and your CGI processing should now be operational.

The default directory for the servlet to locate the actual scripts is WEB-INF/cgi. As has been noted, the WEB-INF directory is protected against casual snooping from browsers, so this is a good place to put CGI scripts, which may contain passwords or other sensitive information. For compatibility with other servers, though, you may prefer to keep the scripts in the traditional directory, /cgi-bin, but be aware that files in this directory may be viewable by the curious web surfer. Also, on Unix, be sure that the CGI script files are executable by the user under which you are running Tomcat.

9. Changing Tomcat's JSP Compiler
In Tomcat 4.1 (and above, presumably), compilation of JSPs is performed by using the Ant program controller directly from within Tomcat. This sounds a bit strange, but it's part of what Ant was intended for; there is a documented API that lets developers use Ant without starting up a new JVM. This is one advantage of having Ant written in Java. Plus, it means you can now use any compiler supported by the javac task within Ant; these are listed in the javac page of the Apache Ant manual. It is easy to use because you need only an <init-param> with a name of "compiler" and a value of one of the supported compiler names:


<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>
      org.apache.jasper.servlet.JspServlet
    </servlet-class>
    <init-param>
      <param-name>logVerbosityLevel</param-name>
      <param-value>WARNING</param-value>
    </init-param>
    <init-param>
      <param-name>compiler</param-name>
      <param-value>jikes</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>
Of course, the given compiler must be installed on your system, and the CLASSPATH may need to be set, depending on which compiler you choose.

10. Restricting Access to Specific Hosts
Sometimes you'll only want to restrict access to Tomcat's web app to only specified host names or IP addresses. This way, only clients at those specified sites will be served content. Tomcat comes with two Valves that you can configure and use for this purpose: RemoteHostValve and RemoteAddrValve.

These Valves allow you to filter requests by host name or by IP address, and to allow or deny hosts that match, similar to the per-directory Allow/Deny directives in Apache httpd. If you run the Admin application, you might want to only allow access to it from localhost, as follows:


<Context path="/path/to/secret_files" ...>
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127.0.0.1" deny=""/>
</Context>

If no allow pattern is given, then patterns that match the deny attribute patterns will be rejected, and all others will be allowed. Similarly, if no deny pattern is given, patterns that match the allow attribute will be allowed, and all others will be denied.





***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:36)
Posted by 아름프로
포레스트 설치는...
http://xml.apache.org/forrest/your-project.html
을 참고해서 설치를 했답니다.
사이트에서 최신바이너리(현재 0.2.1) 받으시고 압축을 푸시면..
fresh-site.zip 이란 파일이 있답니다.

위의 설치 문서에서는.. 이것을 만드는 과정까지 다 나와 있어서..
좀 헛갈려 보일 수 있답니다.
이것을 아무 디렉토리에 압축을 푸시고 디렉토리 구조를 보시면..
위의 설명 문서에서 디렉토리 구조 설명하는 부분과 같은 것을
알 수 있답니다.
여기에서부터는.. 문서의 내용을 보시고 작업을 하시면 됩니다.
실제... 설치하는 것은... 그리 어려운 부분은 아닌데..
사이트를 xml로 실제 만드는 부분은 좀 공부를 하셔야 할껍니다.
저는 기본 UI만 포레스트를 채택하고 링크 구조를 전부 게시판으로
갔기에.. 내용이 아직 채워지지 않은 상태랍니다.

해보시고 잘 안되시면 그 남기세요.. ~




***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:38)
Posted by 아름프로

BLOG main image

카테고리

분류 전체보기 (539)
이야기방 (19)
토론/정보/사설 (16)
IBM Rational (9)
U-IT (0)
SOA/WS/ebXML (110)
개발방법론/모델링 (122)
J2SE (34)
J2EE (60)
DataBase (39)
Open Projects (30)
BP/표준화 (50)
Apache Projects (15)
Web Services (1)
DB (0)
이외 (0)
Jakarta (13)
XML (1)
Web/보안/OS (22)
Tools (7)
AJAX/WEB2.0 (1)
Linux/Unix (1)
영어 (0)
비공개방 (0)

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

달력

«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28

글 보관함

Total :
Today : Yesterday :