You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 13 Next »

This article describes how to develop a Struts1 web application for Geronimo. Besides Struts1 technology, you can also find sample code about usages of JPA and Security annotations.

Because of a problem described in GERONIMO-5020, this sample application is not available as a geronimo plugin for the time being.

This article doesn't intend to serve as a tutorial of developing a Struts1 application. For informations about Struts1 development, see Struts1 web site.

This article is organized into the following sections.

Application overview

Assuming you have accumulated lots of archival data CDs from your computer, each of them includes rich of information you ever collected. The collection of those archived CDs is your valuable knowledge asset, and certainly you want to manage them carefully and orderly for easy references in the future. The DataCDInfo sample application is about to assist you with this task.

With this application, you can register a user, and then login to add records for your archived CDs. You can record detail information that is not suitable to label on CD surface, such as detailed list of data CD content, archived date, and size of the CD.

This application pre-defines some admin roles who will be able to view overall recorded CDs and help retrieve user's forgotten password.

In short words, DataCDInfo is a simple CRUD(Create, Retrieve, Update and Delete) application, which adopts Struts1, JPA, JTA, and security annotation techniques.

Application contents

DataCDInfo uses the typical Java EE application structure, which includes an EJB module, an Web module and an EAR module.

The EJB module

The EJB module includes the major business logic of an application. It consists of JPA entity beans, a stateless session bean, a stateful session bean and some exception classes.

  • Two JPA entity beans: DataCDBean and OwnerBean, which represents Data CD records and Owner records respectively. The relationship bewteen OwnerBean and DataCDBean is 1...*, one owner could have multiple Data CDs.
  • The DataCDInfoJTAImpl is a stateless session bean which implements the business logic of DataCDInfo application, including login, registration/unregistration of the owner, and add/update/removal of data CD records. DataCDInfoLocal and DataCDInfoRemote is the local and remote business interface respectively.
  • The DataCDInfoAdmin is a stateful session, in which there is an EXTENDED persistence context. By default, a container-managed persistence context is of type TRANSACTION. The EXTENDED persistence context can only be initiated within the scope of a stateful session bean.
  • The DataCDInfoAdmin defines two roles "superadmin" and "admin" with security annotation @RolesAllowed. In the code, the role "superadmin" can access all three methods, while the role "admin" can only access "listOwners" method. Another way to define the access is via EJB deployment descriptor ejb-jar.mxl. The configurations in ejb-jar.xml file will override the ones in code already.
    As shown below, the role "admin" also has access to method "listAllDataCDs" besides the method "listOwners" defined in the code because of configuration in its ejb-jar.xml.
    ejb-jar.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <ejb-jar version="3.0"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"> 
        <display-name>DataCDInfo Enterprise Bean Definitions</display-name>
        
        <enterprise-beans>
    	<session>
    		<ejb-name>ejb/DataCDInfoJTAImpl</ejb-name>
    		<business-local>org.apache.geronimo.samples.datacdinfo.core.DataCDInfoLocal</business-local>
    		<business-remote>org.apache.geronimo.samples.datacdinfo.core.DataCDInfoRemote</business-remote>
    		<ejb-class>org.apache.geronimo.samples.datacdinfo.core.DataCDInfoJTAImpl</ejb-class>
                    <!-- Stateful|Stateless -->
    		<session-type>Stateless</session-type>
                    <!-- Who manages transanction? Bean|Container -->
    		<transaction-type>Container</transaction-type>
    	</session>
    	<session>
    		<ejb-name>ejb/DataCDInfoAdmin</ejb-name>
    		<business-local>org.apache.geronimo.samples.datacdinfo.core.DataCDInfoAdminLocal</business-local>
    		<ejb-class>org.apache.geronimo.samples.datacdinfo.core.DataCDInfoAdmin</ejb-class>
                    <!-- Stateful|Stateless -->
    	        <session-type>Stateful</session-type>
                    <!-- Who manages transanction? Bean|Container -->
    		<transaction-type>Container</transaction-type>
    	</session>		
        </enterprise-beans>	
    
        <assembly-descriptor>
    	<method-permission>
    	    <role-name>superadmin</role-name>
    	    <method>
    		<ejb-name>ejb/DataCDInfoAdmin</ejb-name>
    		<method-name>*</method-name>
    	    </method>
    	</method-permission>
    		<!-- In code, role "admin" only has right to access listOwners method
    		but via this xml definition, the role could also access listAllDataCDs method -->
    	<method-permission>
    	    <role-name>admin</role-name>
    		<method>
    	 	    <ejb-name>ejb/DataCDInfoAdmin</ejb-name>
    		    <method-name>listAllDataCDs</method-name>					
    		</method>
    	</method-permission>
        </assembly-descriptor>     
    </ejb-jar>
    
  • A persistence unit is defined via META-INF/persistence.xml as shown below.
    persistence.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
      <persistence-unit name="DataCDInfoUnit" transaction-type="JTA">
        <description>DataCDInfo Persistence Unit Definition</description>
        <jta-data-source>jdbc/DataCDInfoDS</jta-data-source>
        <non-jta-data-source>jdbc/NoTxDataCDInfoDS</non-jta-data-source>	
        <class>org.apache.geronimo.samples.datacdinfo.beans.OwnerBean</class>
        <class>org.apache.geronimo.samples.datacdinfo.beans.DataCDBean</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>      
          <property name="openjpa.Sequence" value="table(Table=OPENJPASEQ, Increment=1)"/>       
          <!--<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>-->
          <!--<property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>-->
          <property name="openjpa.Log" value="File=/tmp/org.apache.openjpa.log, DefaultLevel=WARN, Tool=INFO, Runtime=TRACE, SQL=TRACE"/>
        </properties>
      </persistence-unit>
    </persistence>
    

If the persistence context requires some non-transactional operations, such as table sequence generation, you have to define a non-jta-data-source as well. Otherwise, you will encounter an exception like org.apache.openjpa.persistence.RollbackException: The transaction has been rolled back.

The maven project layout of the EJB module as follows:

|-- pom.xml
`-- src
    `-- main
        |-- java
        |   `-- org
        |       `-- apache
        |           `-- geronimo
        |               `-- samples
        |                   `-- datacdinfo
        |                       |-- beans
        |                       |   |-- DataCDBean.java
        |                       |   `-- OwnerBean.java
        |                       |-- core
        |                       |   |-- DataCDInfoAdmin.java
        |                       |   |-- DataCDInfoAdminLocal.java
        |                       |   |-- DataCDInfoJTAImpl.java
        |                       |   |-- DataCDInfoLocal.java
        |                       |   `-- DataCDInfoRemote.java
        |                       `-- exceptions
        |                           |-- DuplicatedDataCDException.java
        |                           |-- IncorrectPasswordException.java
        |                           |-- InvalidOwnerException.java
        |                           `-- InvalidPasswordException.java
        `-- resources
            `-- META-INF
                |-- ejb-jar.xml
                |-- openejb-jar.xml
                `-- persistence.xml

The Web Module

All Struts1 objects are in the Web module. A typical Struts1 web application uses a configuration file to initialize its resources. Those resources include ActionForms to collect input from users, ActionMappings to direct input to server-side Actions, and ActionForwards to select output pages.(Quoted from Struts1 documentation).

The DataCDInfo application web module consists of:

  • Struts1 ActionForm: DataCDForm and OwnerForm.
    • Those two ActionForm classes extend Struts1 ValidatorForm in order to utilize the convenient validation feature provided by Struts1.
    • You might have noticed that these two classes are very similar to the JPA entity beans. The design is one of requirements in Struts1, so that the view model is separated from the backend business model. To convey data between Struts1 form view and business logic beans, you can use org.apache.commons.beanutils.PropertyUtils method.
  • Struts1 Action: DataCDActions and OwnerActions
    • Those two Action classes extend Struts1 MappingDispatchAction, so that business related actions could be in the same Action class. For more details, check the API doc of MappingDispatchAction.
    • Those two Action classes wrap form data and call the corresponding business operations to persist the data into database.
  • Struts1 resource files: DataCDInfoResource.properties and several DataCDInfoResources_LANG.properties
    • Struts1 uses the standard globalization way that Java language provides to present messages for different locale.
    • The sample includes message resources files for both en_US and zh_CN locales. You can extends the locale support by adding additional locale resource files to the resources directory, and then run a new build for later deployment.
  • Struts1 configuration files: struts-config.xml and validation.xml
    • The struts-config.xml file is the main configuration file of a Struts1 application. All mandatory informations of a Struts1 artifact such as ActionForm, Actions, ActionMapping, and Validator, should all be defined in this file.
    • The{{validation.xml}} file defines the validation rules used by the application. Struts1 provides a simple validator for number and date verification.
  • Struts1 view JSPs: view/jsp/*.jsp
    • The common Struts1 taglibs are used in those JSPs. They are part of standard Struts1 view technologies. Struts1 supports several different view technologies, such as Velocity, Tiles, and etc.

Besides the artifacts of Struts1, there are some other artifacts used for DataCDInfo admin logic operations:

  • DataCDInfoAdminServlet – A servlet used to call security-controlled business methods defined in DataCDInfoAdmin stateful session bean.
  • admin/. – The presentation files of DataCDInfo admin operations
  • auth/. – The files used to FORM authentication. By default, the DataCDInfo application uses BASIC authentication. If you want to see what a FORM authentication looks like, you can modify web.xml as follows:
    part of web.xml
     ...
     <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>geronimo-admin</realm-name>
        <form-login-config>
             <form-login-page>/auth/logon.html</form-login-page>
             <form-error-page>/auth/logonError.html</form-error-page>
        </form-login-config>
     </login-config>
     <!--
     <login-config>
    	<auth-method>BASIC</auth-method>
    	<realm-name>geronimo-admin</realm-name>
     </login-config>
     -->
    ...
    

The maven project layout of the Web module as follows:

|-- pom.xml
`-- src
    `-- main
        |-- java
        |   `-- org
        |       `-- apache
        |           `-- geronimo
        |               `-- samples
        |                   `-- datacdinfo
        |                       `-- web
        |                           |-- DataCDInfoAdminServlet.java
        |                           |-- ListOwnerServlet.java
        |                           `-- struts1
        |                               |-- DataCDActions.java
        |                               |-- DataCDForm.java
        |                               |-- DataCDInfoContextListener.java
        |                               |-- OwnerActions.java
        |                               `-- OwnerForm.java
        |-- resources
        |   |-- DataCDInfoResources.properties
        |   |-- DataCDInfoResources_en_US.properties
        |   |-- DataCDInfoResources_zh.properties.template
        |   `-- DataCDInfoResources_zh_CN.properties
        `-- webapp
            |-- META-INF
            |   |-- LICENSE
            |   |-- MANIFEST.MF
            |   `-- NOTICE
            |-- WEB-INF
            |   |-- geronimo-web.xml
            |   |-- struts-config.xml
            |   |-- validation.xml
            |   `-- web.xml
            |-- admin
            |   |-- adminhome.html
            |   |-- showCDs.jsp
            |   |-- showOwners.jsp
            |   `-- showPasswd.jsp
            |-- auth
            |   |-- logon.html
            |   `-- logonError.html
            |-- header.html
            |-- index.html
            `-- view
                `-- jsp
                    |-- AddCD.jsp
                    |-- ListCDs.jsp
                    |-- Logon.jsp
                    |-- Logout.jsp
                    |-- Register.jsp
                    |-- RemoveCD.jsp
                    `-- UpdateCD.jsp

The EAR module

The EAR module contains database creation scripts and the application deployment plan. The application deployment plan will override the duplicate configurations defined in the EJB module and Web module.

In the application deployment plan, there are definitions about the web context root and the security realm used to authenticate the admin operations.

Web module definition in geronimo-application.xml
...
<module>
    <web>DataCDInfo-JTA-war.war</web>
    <web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1">
	<context-root>/DataCDInfo</context-root>
	<security-realm-name>geronimo-admin</security-realm-name>
    </web-app>
</module>
...

The DataCDInfo application uses the default geronimo security realm geronimo-admin, which is a .properties file-based realm. To enable "superadmin" role used by this application, these .properties files in /var/security must be modified before starting Geronimo server:

Add a new group in <geronimo_home>/var/security/groups.properties
...
superadmin=superman
...
Set the password for the new user in <geronimo_home>/var/security/users.properties
...
superman=password
...

The plain text password will be encrypted when the geronimo server restarts.

Two datasources are defined in the deployment plan. jdbc/DataCDInfoDS is for JTA scenarioa, and jdbc/NoTxDataCDInfoDS for non-JTA.

Datasources in geronimo-application.xml
...
<ext-module>
    <connector>DataCDInfoDataSource</connector>
    <external-path xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2">
	<dep:groupId>org.tranql</dep:groupId>
	<dep:artifactId>tranql-connector-derby-embed-xa</dep:artifactId>
	<dep:type>rar</dep:type>
    </external-path>
    <connector xmlns="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2">
	<resourceadapter>
	    <outbound-resourceadapter>
		<connection-definition>
	  	    <connectionfactory-interface>javax.sql.DataSource</connectionfactory-interface>
		    <connectiondefinition-instance>
			<name>jdbc/DataCDInfoDS</name>
			<config-property-setting name="UserName"></config-property-setting>
                        <config-property-setting name="Password"></config-property-setting>
                        <config-property-setting name="DatabaseName">cdinfodb</config-property-setting>
                        <config-property-setting name="CreateDatabase">true</config-property-setting>
		    <connectionmanager>
			<xa-transaction>
		  	    <transaction-caching />
			</xa-transaction>
			<single-pool>
			    <max-size>100</max-size>
			    <min-size>0</min-size>
			    <blocking-timeout-milliseconds>5000</blocking-timeout-milliseconds>
			    <idle-timeout-minutes>30</idle-timeout-minutes>
			    <match-one />
			</single-pool>
		    </connectionmanager>
		</connectiondefinition-instance>
		<!-- This non-transaction data source is for sequence generation use. Without it,
		the geronimo will throw exception when persisting entities which require sequence
		generation. -->
		<connectiondefinition-instance>
                    <name>jdbc/NoTxDataCDInfoDS</name>
                    <config-property-setting name="UserName"></config-property-setting>
                    <config-property-setting name="Password"></config-property-setting>
                    <config-property-setting name="DatabaseName">cdinfodb</config-property-setting>
                    <config-property-setting name="CreateDatabase">true</config-property-setting>
                         <connectionmanager>
                             <no-transaction/>
                             <single-pool>
                                 <max-size>10</max-size>
                                 <min-size>0</min-size>
                                 <blocking-timeout-milliseconds>5000</blocking-timeout-milliseconds>
                                 <idle-timeout-minutes>30</idle-timeout-minutes>
                                 <match-one/>
                             </single-pool>
                         </connectionmanager>
               </connectiondefinition-instance>
	     </connection-definition>
          </outbound-resourceadapter>
        </resourceadapter>
    </connector>
</ext-module>
...

To map the application security roles to Geronimo security roles, the deployment plan must include configurations as below:

Security Roles Mapping in geronimo-application.xml
...
  <security xmlns:sec="http://geronimo.apache.org/xml/ns/security-2.0">
    <sec:default-principal>
      <sec:principal class="org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal" name="anonymous"/>
    </sec:default-principal>
    <sec:role-mappings>
      <sec:role role-name="admin">
        <sec:principal class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal" name="admin"/>
        <sec:principal class="org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal" name="system"/>
      </sec:role>
      <sec:role role-name="superadmin">
        <sec:principal class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal" name="superadmin"/>
        <sec:principal class="org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal" name="superman"/>
      </sec:role>
    </sec:role-mappings>
  </security>
...

Besides the default deployment plan, there is another plan for MySQL database. You can enable the database by using deploy command:

Deploy DataCDInfo with MySQL database

deploy deploy DataCDInfo-JTA-ear-2.2.ear geronimo-application-mysql.xml

You must install proper mysql jdbc driver into Geronimo repository first before deploying the DataCDInfo application with MySQL deployment plan geronimo-application-mysql.xml.

The maven project layout of the EAR module as follows:

|-- pom.xml
`-- src
    `-- main
        `-- resources
            |-- DataCDInfo_tables_derby.sql
            |-- DataCDInfo_tables_mysql.sql
            `-- META-INF
                |-- application.xml
                |-- geronimo-application-mysql.xml
                `-- geronimo-application.xml

Run Application

Using geronimo admin console to deploy the application.

Running DataCDInfo

If you just use "admin" role(for example, use "system" account defined in the geronimo-admin realm) to pass the authentication of DataCDInfo Admin resources, you will see an exception like this when trying to view the owner's password.

  • No labels