This article describes about how to develop a Struts1 web application for Geronimo. Besides Struts1 technology, you also can find some sample code about JPA and Security annotation.
Because of the JIRA, the sample is not available as a geronimo plugin at the written time.
This article won't serve as a tutorial of developing a Struts1 application. For more information about Struts1 development, refer to Struts1 web site.
This article is organized into the following sections.
Application overview
Assuming you have accumulated lot of archival data CDs from your computer, each of them includes rich of information you ever collected. The collection of the archived CDs is your valuable knowledge asset, so you want to manage them carefully and organizedly for future easy references. The DataCDInfo is an application to assist you to manage the information of your archival data CDs.
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, archival date, and CD size.
This application set some admin roles to be able to view overall recorded CDs and help retrieve user's password if there is a forgotten.
In a short summary, 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: an EJB module, an Web module, and an EAR module.
The maven layout of each module as follows:
The EJB module
There are two JPA entity beans: DataCDBean and OwnerBean, represents Data CD records and Owner records respectively. The relation 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 owner, and add/update/remove 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 a scope of a stateful session bean.
The DataCDInfoAdmin defines two roles "superadmin" and "admin" with security annotation @RolesAllowed. In the code, role "superadmin" can access all of three methods, while role "admin" can only access "listOwners" method. Another way to define the access is via EJB deployment descriptor "ejb-jar.mxl". The definition in ejb-jar.xml overrides the one in code.
As the definition of "ejb-jar.xml", the role "admin" also has access to method "listAllDataCDs" besides the method "listOwners" defined in the code.
<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <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>
|-- 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
|-- 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:
|-- pom.xml `-- src `-- main `-- resources |-- DataCDInfo_tables_derby.sql |-- DataCDInfo_tables_mysql.sql `-- META-INF |-- application.xml |-- geronimo-application-mysql.xml `-- geronimo-application.xml
Steps to
<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <security-constraint> <web-resource-collection> <web-resource-name>employee</web-resource-name> <url-pattern>/employee/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>employee</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>manager</web-resource-name> <url-pattern>/manager/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>manager</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <realm-name>TimeReportRealm</realm-name> <form-login-config> <form-login-page>/login/login.jsp</form-login-page> <form-error-page>/login/login_error.jsp</form-error-page> </form-login-config> </login-config> <security-role> <role-name>employee</role-name> </security-role> <security-role> <role-name>manager</role-name> </security-role> <servlet> <display-name>AddTimeRecordServlet</display-name> <servlet-name>AddTimeRecordServlet</servlet-name> <servlet-class>org.apache.geronimo.samples.timereport.web.AddTimeRecordServlet</servlet-class> </servlet> <servlet> <display-name>AddEmployeeServlet</display-name> <servlet-name>AddEmployeeServlet</servlet-name> <servlet-class>org.apache.geronimo.samples.timereport.web.AddEmployeeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AddTimeRecordServlet</servlet-name> <url-pattern>/employee/add_timerecord</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>AddEmployeeServlet</servlet-name> <url-pattern>/manager/add_employee</url-pattern> </servlet-mapping> </web-app>
The Geronimo deployment plan ( plan.xml found after building the project at timereport/timereport-jetty/target/resources/META-INF/plan.xml) includes the Geronimo specific security configuration including the security realm configuration and the principal-role mapping relating the principals from the security realm to the application roles defined above in web.xml This project uses two roles, manager and employee. There is a business rule that every manager is an employee. This is enforced through the principal-role mapping: both the EmployeeGroup and ManagerGroup imply the app specific employee role.
<?xml version="1.0" encoding="UTF-8"?> <!--Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.--> <!--$Rev: 497879 $ $Date: 2007-01-19 12:11:01 -0500 (Fri, 19 Jan 2007) $--> <web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1"> <dep:environment xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2"> <dep:moduleId> <dep:groupId>org.apache.geronimo.samples</dep:groupId> <dep:artifactId>timereport-jetty</dep:artifactId> <dep:version>2.1-SNAPSHOT</dep:version> <dep:type>car</dep:type> </dep:moduleId> <dep:dependencies> <dep:dependency> <dep:groupId>org.apache.geronimo.samples</dep:groupId> <dep:artifactId>sample-datasource</dep:artifactId> <dep:version>2.1-SNAPSHOT</dep:version> <dep:type>car</dep:type> </dep:dependency> <dep:dependency> <dep:groupId>org.apache.geronimo.configs</dep:groupId> <dep:artifactId>jasper</dep:artifactId> <dep:version>2.1</dep:version> <dep:type>car</dep:type> </dep:dependency> <dep:dependency> <dep:groupId>org.apache.geronimo.configs</dep:groupId> <dep:artifactId>jetty6</dep:artifactId> <dep:version>2.1</dep:version> <dep:type>car</dep:type> </dep:dependency> </dep:dependencies> <dep:hidden-classes/> <dep:non-overridable-classes/> </dep:environment> <context-root>/timereport</context-root> <security-realm-name>TimeReportRealm</security-realm-name> <security> <default-principal realm-name="TimeReportRealm"> <principal name="anonymous" class="org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal"/> </default-principal> <role-mappings> <role role-name="employee"> <realm realm-name="TimeReportRealm"> <principal name="EmployeeGroup" class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal"/> </realm> <realm realm-name="TimeReportRealm"> <principal name="ManagerGroup" class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal"/> </realm> </role> <role role-name="manager"> <realm realm-name="TimeReportRealm"> <principal name="ManagerGroup" class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal"/> </realm> </role> </role-mappings> </security> <gbean name="DBInitialization" class="org.apache.geronimo.connector.DatabaseInitializationGBean"> <!--<attribute name="testSQL">select * from users</attribute>--> <attribute name="path">TimeReportDB.sql</attribute> <reference name="DataSource"> <name>SampleTxDatasource</name> </reference> </gbean> <gbean name="TimeReportRealm" class="org.apache.geronimo.security.realm.GenericSecurityRealm"> <attribute name="realmName">TimeReportRealm</attribute> <reference name="ServerInfo"> <name>ServerInfo</name> </reference> <xml-reference name="LoginModuleConfiguration"> <log:login-config xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-1.1"> <log:login-module control-flag="REQUIRED" wrap-principals="false"> <log:login-domain-name>TimeReportRealm</log:login-domain-name> <log:login-module-class>org.apache.geronimo.security.realm.providers.SQLLoginModule</log:login-module-class> <log:option name="dataSourceName">SampleNoTxDatasource</log:option> <log:option name="userSelect">select userid, password from users where userid=?</log:option> <log:option name="groupSelect">select userid, groupname from usergroups where userid=?</log:option> </log:login-module> </log:login-config> </xml-reference> </gbean> </web-app>
To restrict access to the Add Employee functionality from Time Report page, programmatic authentication has beeen used as in indicated below.
... <BR> <%if(request.isUserInRole("manager")){%> <A href="../manager/">Add Employees</A> <BR> ...
Testing of the Sample Application
To test the sample application open a browser and type http://localhost:8080/timereport. It will forward to the Welcome page of the application.
User can access Time Report page providing username as emp1 and password with pass1. To login to the application as a Manager provide mgm1 and pass3 credentials.
Summary
This article has shown you how to deploy web application in to the Geronimo Application server with J2EE declarative security features. You followed step-by-step instructions to build, deploy and test the sample application.
Some highlights of the article are:
- Apache Geronimo provides two different web containers namely Jetty and Tomcat.
- Create a database to hold security data with built-in Derby.
- Define security roles in Geronimo Web applications.
- Deploy deployment plans and web archives using the Geronimo Console.