Thursday, August 23, 2007

Monday, August 20, 2007

Handling Security with Ajax, DWR and Acegi

This is an extension of a previous post that described how to secure your method calls using Acegi security. Here, I will go through how to secure your Asynchronous calls, using the same example with some modifications to include Ajax calls using Direct Web Remoting (DWR).
  1. Create the example project as shown in "Spring security with Acegi Security Framework". This will be the starting point.
  2. Create the SecureDAO object.
    package test;

    public class SecureDAO {
    public String create() {
    System.out.println("Create");
    return "create";
    }

    public String read() {
    System.out.println("read");
    return "read";
    }

    public String update() {
    System.out.println("update");
    return "update";
    }
    }
  3. Update the applicationContext.xml file to include the security definitions by adding the following bean definitions as shown below
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>

    <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
    <property name="filterInvocationDefinitionSource">
    <value>
    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    PATTERN_TYPE_APACHE_ANT
    /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
    </value>
    </property>
    </bean>

    <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>

    <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
    <constructor-arg value="/index.jsp"/>
    <constructor-arg>
    <list>
    <ref bean="rememberMeServices"/>
    <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
    </list>
    </constructor-arg>
    </bean>

    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="authenticationFailureUrl" value="/login.jsp?errorId=1"/>
    <property name="defaultTargetUrl" value="/"/>
    <property name="filterProcessesUrl" value="/j_acegi_security_check"/>
    <property name="rememberMeServices" ref="rememberMeServices"/>
    </bean>

    <bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>

    <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="rememberMeServices" ref="rememberMeServices"/>
    </bean>

    <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
    <property name="key" value="changeThis"/>
    <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
    </bean>

    <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
    <property name="authenticationEntryPoint">
    <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
    <property name="loginFormUrl" value="/login.jsp"/>
    <property name="forceHttps" value="false"/>
    </bean>
    </property>
    <property name="accessDeniedHandler">
    <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
    <property name="errorPage" value="/denied.jsp"/>
    </bean>
    </property>
    </bean>

    <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="accessDecisionManager">
    <bean class="org.acegisecurity.vote.AffirmativeBased">
    <property name="allowIfAllAbstainDecisions" value="false"/>
    <property name="decisionVoters">
    <list>
    <bean class="org.acegisecurity.vote.RoleVoter"/>
    <bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
    </list>
    </property>
    </bean>
    </property>
    <property name="objectDefinitionSource">
    <value>
    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    PATTERN_TYPE_APACHE_ANT
    /secure/admin/**=ROLE_ADMIN
    /secure/**=IS_AUTHENTICATED_REMEMBERED
    /**=IS_AUTHENTICATED_ANONYMOUSLY
    </value>
    </property>
    </bean>

    <bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
    <property name="userDetailsService" ref="userDetailsService"/>
    <property name="tokenValiditySeconds" value="1800"></property>
    <property name="key" value="changeThis"/>
    </bean>

    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
    <property name="providers">
    <list>
    <ref local="daoAuthenticationProvider"/>
    <bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
    <property name="key" value="changeThis"/>
    </bean>
    <bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
    <property name="key" value="changeThis"/>
    </bean>
    </list>
    </property>
    </bean>

    <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
    <property name="userDetailsService" ref="userDetailsService"/>
    <property name="userCache">
    <bean class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
    <property name="cache">
    <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <property name="cacheManager">
    <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
    </property>
    <property name="cacheName" value="userCache"/>
    </bean>
    </property>
    </bean>
    </property>
    </bean>

    <bean id="userDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
    <property name="userProperties">
    <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="location" value="/WEB-INF/users.properties"/>
    </bean>
    </property>
    </bean>

    <bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/>

    <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
    <property name="authenticationManager">
    <ref bean="authenticationManager" />
    </property>
    <property name="accessDecisionManager">
    <bean class="org.acegisecurity.vote.AffirmativeBased">
    <property name="allowIfAllAbstainDecisions" value="false" />
    <property name="decisionVoters">
    <list>
    <bean class="org.acegisecurity.vote.RoleVoter" />
    <bean class="org.acegisecurity.vote.AuthenticatedVoter" />
    </list>
    </property>
    </bean>
    </property>
    <property name="objectDefinitionSource">
    <value>
    test.SecureDAO.*=IS_AUTHENTICATED_REMEMBERED
    test.SecureDAO.u=ROLE_ADMIN
    </value>
    </property>
    </bean>


    <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="interceptorNames">
    <list>
    <value>methodSecurityInterceptor</value>
    </list>
    </property>
    <property name="beanNames">
    <list>
    <value>secureDAO</value>
    </list>
    </property>
    </bean>

    <bean id="secureDAO" class="test.SecureDAO" />
    </beans>
  4. Update the Web deployment descriptor to forward DWR requests to the DWR Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <display-name>DWRSpring</display-name>
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
    <display-name>SpringListener</display-name>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <filter>
    <filter-name>Acegi Filter Chain Proxy</filter-name>
    <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
    <init-param>
    <param-name>targetClass</param-name>
    <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
    </init-param>
    </filter>

    <filter-mapping>
    <filter-name>Acegi Filter Chain Proxy</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
    <init-param>
    <param-name>debug</param-name>
    <param-value>true</param-value>
    </init-param>
    </servlet>

    <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    </web-app>

  5. In the DWR configuration file, set the creator to Spring
    <!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
    <dwr>
    <allow>
    <convert match="java.lang.Exception" converter="exception"/>
    <create creator="spring" javascript="secureDAO">
    <param name="beanName" value="secureDAO"/>
    </create>
    </allow>
    </dwr>
    Note:
    • The converter is used to convert the Java exception to Javascript exception.
  6. The main change will be made to the secure/authenticatedusers.jsp file. This will use DWR to make Ajax requests. Here is the code for the file
    <%@ page import="org.acegisecurity.context.SecurityContextHolder"%>
    <html>
    <head>

    <script type='text/javascript' src='/DWRSpring/dwr/interface/secureDAO.js'></script>
    <script type="text/javascript" src="../dwr/engine.js"> </script>
    <script type="text/javascript" src="../dwr/util.js"> </script>
    <script>
    dwr.engine.setErrorHandler(errorHandlerFn);
    function update() {
    var name = dwr.util.getValue("method");
    switch(name) {
    case "create":
    secureDAO.create(callBackFn)
    break;
    case "read":
    secureDAO.read(callBackFn);
    break;
    case "update":
    secureDAO.update(callBackFn);
    break;
    }

    }

    function callBackFn(str) {
    dwr.util.setValue("selectedAction","Server Returned : " + str);
    }

    function errorHandlerFn(message, exception) {
    dwr.util.setValue("selectedAction", "Error : " + message);
    }
    </script>

    </head>
    <body>
    <h1>Welcome: <%=SecurityContextHolder.getContext().getAuthentication().getName()%></h1>
    <p><a href="../">Home</a>
    <form name="testForm" action=""><select name="method" onchange="update()">
    <option value=""></option>
    <option value="create">create</option>
    <option value="read">read</option>
    <option value="update">update</option>
    </select></form>

    <div id="selectedAction"></div>
    <p><a href="../j_acegi_logout">Logout</a></p>
    </body>
    </html>
    Note:
    • The classes that are exposed through DWR will be available through the /WEB_APP_NAME/dwr/interface/JAVASCRIPT_NAME.js files.
      <script type='text/javascript' src='/DWRSpring/dwr/interface/secureDAO.js'></script>
    • The setErrorhandler call sets the global error handling function.
      dwr.engine.setErrorHandler(errorHandlerFn);
      Alternatively, the error handling function can be set for individual method calls (as described in DWR documentationa
      Remote.method(params, {
      callback:function(data) { ... },
      errorHandler:function(errorString, exception) { ... }
      });
    • util.js file contains utility functions for getting and setting values for the document elements.
  7. Make sure you have the following Jar files in your classpath:
    • acegi-security-1.0.3.jar
    • ant-junit.jar
    • cglib-nodep-2.1_3.jar
    • commons-codec-1.3.jar
    • commons-logging.jar
    • dwr.jar
    • ehcache-1.2.3.jar
    • jstl.jar
    • spring.jar
    • standard.jar

Wednesday, May 09, 2007

OpenJDK and JavaFX

Java is now completely open source. The JDK source code is now available through the OpenJDK project. According to the marketing manager of the OpenJDK project Rich Sands, Developers can,
... learn how the JDK is put together, fix that bug that's been 
driving you nuts, join the conversations in the mailing lists,
start or participate in projects to improve the implementation.
It's good to see that the governing body consists of not just Sun employees.

On a related note, Sun has announced the new JavaFX product family. Following Adobe's Flex, and Microsoft's Silverlight, JavaFX script is another product for building rich internet applicaitons. JavaFX Mobile is a complete mobile operating and application environment built around Java and Linux. JavaFX Mobile includes support for Java ME applications and other standard Java APIs.

Monday, May 07, 2007

Securing EJB 3.0 Beans

The Java EE 5 Security services are provided by the container and can be implemented using declarative or programmatic techniques. In addition to declarative and programmatic ways to implement security (in J2EE), Java EE 5 supports the use of metadata annotations for security. This post will describe how to secure EJB 3.0 beans. The post consists of a simple EJB, with a web client. In order to run the example, follow these steps.
Create Users in Glassfish
  1. Go to Configuration->Security->Realms->file in the Glassfish admin console.
  2. In the file realm, click on manage users.
  3. Add new users by clicking on add there.

The EJB Component
  1. Start with a Simple Java project in Eclipse.
  2. Remote Interface
    package ejb;

    import javax.ejb.Remote;

    @Remote
    public interface DABean {
    public String create();

    public String read();

    public String update();

    public String delete();
    }
    ejb/DABean.java
  3. The Bean:
    package ejb;

    import javax.annotation.security.DeclareRoles;
    import javax.annotation.security.RolesAllowed;
    import javax.ejb.Stateless;

    @Stateless (mappedName = "ejb/secureEJB")
    @DeclareRoles({"emp","guest"})

    public class SecureEJB implements DABean {

    @RolesAllowed({"emp","guest"})
    public String create() {
    return "create";
    }

    @RolesAllowed({"emp","guest"})
    public String read() {
    return "read";
    }

    @RolesAllowed("emp")
    public String update() {
    return "update";
    }

    @RolesAllowed("emp")
    public String delete() {
    return "delete";
    }

    }
    ejb/SecureEJB.java
    • The declaredRoles and RolesAllowed annotations take a string array as a parameter.
  4. Deployment descriptor:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 EJB 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
    <sun-ejb-jar>
    <security-role-mapping>
    <role-name>guest</role-name>
    <group-name>guest</group-name>
    </security-role-mapping>

    <security-role-mapping>
    <role-name>emp</role-name>
    <group-name>employee</group-name>
    </security-role-mapping>

    <enterprise-beans>
    <unique-id>0</unique-id>
    <ejb>
    <ejb-name>SecureEJB</ejb-name>
    <jndi-name>ejb/secureEJB</jndi-name>
    <gen-classes />
    </ejb>
    </enterprise-beans>
    </sun-ejb-jar>
    META-INF/sun-ejb-jar.xml

The Web Client
For a little bit more detail explanation on the Web Application, see the previous post Securing Java EE 5 Web Applications
  1. The EJB Client Jar file: When you deploy the EJB application in Glassfish, it creates a corresponding EJB Client jar file for the EJB component, which can be used in the clients. The file will created in the following directory.
    GLASSFISH_HOME\domains\DOMAIN_NAME/generated\xml/j2ee-modules/APPLICATION_NAME
  2. Selection page
    <html>
    <body>
    <h1>Home Page</h1>
    Anyone can view this page.

    <form action="securityServlet"><select name="method">
    <option value="create">create</option>
    <option value="read">read</option>
    <option value="update">update</option>
    <option value="delete">delete</option>
    </select> <input type="submit" name="submit" /></form>
    </body>
    </html>
    index.jsp
  3. Servlet
    package servlets;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.annotation.security.DeclareRoles;
    import javax.ejb.EJB;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import ejb.DABean;

    @DeclareRoles("emp")
    public class SecurityServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

    @EJB(name = "timerBean", mappedName = "corbaname:iiop:localhost:3700#ejb/secureEJB")
    private DABean daBean;

    public SecurityServlet() {
    super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    String method = request.getParameter("method");
    try {
    String result = "";
    if (method.equals("create")) {
    result = daBean.create();
    }
    if (method.equals("read")) {
    result = daBean.read();
    }

    if (method.equals("update")) {
    result = daBean.update();
    }

    if (method.equals("delete")) {
    result = daBean.delete();
    }

    out.println(request.getUserPrincipal() + " is an Authorized User");
    } catch (Exception e) {
    e.printStackTrace();
    out.println(request.getUserPrincipal() + " is not an Authorized to see this page.");
    }
    }
    }
    SecurityServlet.java
  4. Deployment descriptor
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5" 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/web-app_2_5.xsd">
    <display-name>Java5Security</display-name>

    <servlet>
    <description></description>
    <display-name>SecurityServlet</display-name>
    <servlet-name>SecurityServlet</servlet-name>
    <servlet-class>servlets.SecurityServlet</servlet-class>
    <security-role-ref>
    <role-name>emp</role-name>
    <role-link>emp</role-link>
    </security-role-ref>
    </servlet>
    <servlet-mapping>
    <servlet-name>SecurityServlet</servlet-name>
    <url-pattern>/securityServlet</url-pattern>
    </servlet-mapping>


    <login-config>
    <auth-method>FORM</auth-method>
    <realm-name>file</realm-name>
    <form-login-config>
    <form-login-page>/login.jsp</form-login-page>
    <form-error-page>/error.jsp</form-error-page>
    </form-login-config>
    </login-config>

    <security-constraint>
    <web-resource-collection>
    <web-resource-name>Protected Area</web-resource-name>
    <url-pattern>/*</url-pattern>
    <http-method>PUT</http-method>
    <http-method>DELETE</http-method>
    <http-method>GET</http-method>
    <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
    <role-name>guest</role-name>
    <role-name>emp</role-name>
    </auth-constraint>
    </security-constraint>

    <security-constraint>
    <web-resource-collection>
    <web-resource-name>Protected Area</web-resource-name>
    <url-pattern>/secure/*</url-pattern>
    <http-method>PUT</http-method>
    <http-method>DELETE</http-method>
    <http-method>GET</http-method>
    <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
    <role-name>emp</role-name>
    </auth-constraint>
    </security-constraint>
    <!-- Security roles referenced by this web application -->
    <security-role>
    <role-name>guest</role-name>
    </security-role>
    <security-role>
    <role-name>emp</role-name>
    </security-role>

    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    </web-app>
    web.xml
  5. Glassfish Deployment descriptor
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-1.dtd">
    <sun-web-app>
    <context-root>/Java5Security</context-root>
    <security-role-mapping>
    <role-name>guest</role-name>
    <group-name>guest</group-name>
    </security-role-mapping>
    <security-role-mapping>
    <role-name>emp</role-name>
    <group-name>employee</group-name>

    </security-role-mapping>
    </sun-web-app>
    sun-web.xml
Environment: This example was run on Glassfish V2 Build 41 (Glassfish V2 Beta 2).

Tuesday, May 01, 2007

Java EE 5 on WebSphere

While the next version of WAS (v7.0) with Java EE 5, is yet to be released, you can now test some of the features such as EJB 3.0 with WebSphere software early programs. The new features are available as feature packs for the current WebSphere Application Server (v6.1).

Friday, April 13, 2007

Customize Acegi SecurityContext

This post describes how to modify the User details stored in the Acegi Security Context.

Acegi Security uses the SecurityContextHolder object to store details of the current security context of the application. The SecurityContext holds the details of the authenticated principal in an Authentication object. By default the SecurityContextHolder uses a ThreadLocal to store these details, so that they will be available all methods in the current thread of execution.In order to obtain the Principal, you would use the following line
Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Usually, the principal is an object of the type UserDetails. The UserDetails object is sort of an adapter between the security database and Acegi SecurityContextHolder. Acegi uses a UserDetailsSevice to build the UserDetails object. In order to modify the data stored Security Context of Acegi, you either have to
  • setContext() method of the SecurityContextHolder. The SecurityContext object can be set to hold an Authentication object. This will mean that you have to add some additional security code to your application code.
  • Implement the UserDetailsService and UserDetails interfaces. This way you can keep security code seperated from the application code.
This post will describe how to implement UserDetailsService and UserDetails Objects. Here's what to do to implement a UserDetailsService.
  1. Start with the implementing acegi security posts part 1 and part 2.
  2. Create the UserDetailsService:
    package authentication;

    import java.util.HashMap;
    import java.util.Map;

    import org.acegisecurity.GrantedAuthority;
    import org.acegisecurity.userdetails.UserDetails;
    import org.acegisecurity.userdetails.UserDetailsService;
    import org.acegisecurity.userdetails.UsernameNotFoundException;
    import org.springframework.dao.DataAccessException;

    public class MyUserDetailsService implements UserDetailsService {

    private Map users = init();

    private Map init() {
    Map tUsers = new HashMap();

    tUsers.put("scott", new User("scott", "tiger", "ROLE_USER"));
    tUsers.put("harry", new User("harry", "potter", "ROLE_ADMIN"));
    tUsers.put("frodo", new User("frodo", "baggins", "ROLE_USER"));

    return tUsers;
    }

    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException, DataAccessException {
    User user = (User) users.get(s);
    GrantedAuthority authority = new MyGrantedAuthority(user.getRole());
    UserDetails userDetails = new MyUserDetails(new GrantedAuthority[] { authority }, user.getUserId(), user.getPassword(), "Additional Data");
    return userDetails;
    }

    }
    MyUserDetailsService.java

    Notice that all the user data is defined in this class. The UserDetailsService has to return a UserDetails object using the User name.
  3. Create the UserDetails class:
    package authentication;

    import org.acegisecurity.GrantedAuthority;
    import org.acegisecurity.userdetails.UserDetails;

    public class MyUserDetails implements UserDetails {


    private GrantedAuthority[] authorities = null;
    private String password = null;
    private String username = null;
    private String additionalData = null;


    public MyUserDetails(GrantedAuthority[] authorities, String password, String username, String additionalData) {
    super();
    this.authorities = authorities;
    this.password = password;
    this.username = username;
    this.additionalData = additionalData;
    }

    public GrantedAuthority[] getAuthorities() {
    return authorities;
    }

    public String getPassword() {
    return password;
    }

    public String getUsername() {
    return username;
    }

    public boolean isAccountNonExpired() {
    return true;
    }

    public boolean isAccountNonLocked() {
    return true;
    }

    public boolean isCredentialsNonExpired() {
    return true;
    }

    public boolean isEnabled() {
    return true;
    }
    }
    MyUserDetails.java
  4. The User class:
    package authentication;

    public class User {

    private String userId;

    private String password;

    private String role;

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    public String getRole() {
    return role;
    }

    public void setRole(String role) {
    this.role = role;
    }

    public String getUserId() {
    return userId;
    }

    public void setUserId(String userId) {
    this.userId = userId;
    }

    public User(String userId, String password, String role) {
    super();
    this.userId = userId;
    this.password = password;
    this.role = role;
    }

    }
    User.java
  5. Modify the applicationContext.xml file: Modify the userDetailsService bean in the application context to as shown below
    <bean id="userDetailsService" class="authentication.MyUserDetailsService">
    </bean>
  6. The GrantedAuthority class: The MyGrantedAuthority class is typically used to store application roles.
    package authentication;

    import org.acegisecurity.GrantedAuthority;

    public class MyGrantedAuthority implements GrantedAuthority {

    private String authority = null;

    public MyGrantedAuthority(String authority) {
    this.authority = authority;
    }
    public String getAuthority() {
    return authority;
    }

    }
    MyGrantedAuthority.java

Tuesday, April 10, 2007

Native Queries with Hibernate Annotations

Hibernate EntityManager implements the programming interfaces and lifecycle rules as defined by the EJB3 persistence specification. Together with Hibernate Annotations, this wrapper implements a complete (and standalone) EJB3 persistence solution on top of the mature Hibernate core. In this post I will describe how map native queries (plain SQL) using Hibernate Annotations. Hibernate Annotations supports the use of Native queries through the @NamedNativeQuery and the @SqlResultSetMapping annotations.
  • @NamedNativeQuery: Specifies a native SQL named query.
  • @SqlResultSetMapping: Used to specify the mapping of the result of a native SQL query.
You will not need any EJB container support. At a minimum, you will need Hibernate core and Hibernate Annotations. The entire list of required Jar files is provided at the end.
  1. The Hibernate Configuration File: Nothing new here. Except that there are no mappings defined. I used programmatic declaration of mapping for this example as shown in the following steps.
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>
    <session-factory>
    <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
    <property name="connection.url">jdbc:oracle:thin:@localhost:1521/orcl</property>
    <property name="connection.username">scott</property>
    <property name="connection.password">tiger</property>
    <property name="dialect">org.hibernate.dialect.Oracle9Dialect</property>
    <property name="hibernate.current_session_context_class">thread</property>
    </session-factory>
    </hibernate-configuration>
    hibernate.cfg.xml
  2. The Entity class:
    package data;

    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.EntityResult;
    import javax.persistence.FieldResult;
    import javax.persistence.Id;
    import javax.persistence.NamedNativeQuery;
    import javax.persistence.SqlResultSetMapping;

    @Entity
    @SqlResultSetMapping(name = "implicit", entities = @EntityResult(entityClass = data.Employee.class))
    @NamedNativeQuery(name = "implicitSample", query = "select e.empno empNumber, e.ename empName, e.job empJob, e.sal empSalary, salg.grade empGrade from emp e, salgrade salg where e.sal between salg.losal and salg.HISAL", resultSetMapping = "implicit")
    //@SqlResultSetMapping(name = "explicit", entities = { @EntityResult(entityClass = data.Employee.class, fields = {
    // @FieldResult(name = "empNumber", column = "empno"),
    // @FieldResult(name = "empName", column = "ename"),
    // @FieldResult(name = "empJob", column = "job"),
    // @FieldResult(name = "empSalary", column = "sal"),
    // @FieldResult(name = "empGrade", column = "grade") }) })
    //@NamedNativeQuery(name = "implicitSample",
    // query = "select e.empno empno, e.ename ename, e.job job, e.sal sal, salg.grade grade from emp e, salgrade salg where e.sal between salg.losal and salg.HISAL", resultSetMapping = "explicit")
    public class Employee {

    private String empNumber;

    private String empName;

    private String empJob;

    private Double empSalary;

    private int empGrade;

    @Column
    @Id
    public int getEmpGrade() {
    return empGrade;
    }

    public void setEmpGrade(int empGrade) {
    this.empGrade = empGrade;
    }

    @Column
    public String getEmpJob() {
    return empJob;
    }

    public void setEmpJob(String empJob) {
    this.empJob = empJob;
    }

    @Column
    public String getEmpName() {
    return empName;
    }

    public void setEmpName(String empName) {
    this.empName = empName;
    }

    @Column
    public String getEmpNumber() {
    return empNumber;
    }

    public void setEmpNumber(String empNumber) {
    this.empNumber = empNumber;
    }

    @Column
    public Double getEmpSalary() {
    return empSalary;
    }

    public void setEmpSalary(Double empSalary) {
    this.empSalary = empSalary;
    }

    }
    Employee.java
    • The implitic mapping (the uncommented @NamedNativeQuery and @SqlResultSetMapping declarations are used for implicitly mapping the ResultSet to the entity class. Note that the SQL column names match the field names in the class. If the names do not match then you can set the name attribute of the @Column annotation to the column name in the query.
    • The commented @NamedNativeQuery and the @SqlResultSetMapping declarations explicitly map the fields to the columns. This will come in handy when using joins and composite keys etc.
    • Note the package definitions refer to javax.persistence and not the hibernate packages. If the packages are not declared properly, you will most likely end up with some exceptions like the following
      org.hibernate.hql.ast.QuerySyntaxException: Employee is not mapped.
      While there are other causes for this exception, the package declarations did cause a little trouble for me.
  3. The Client:
    import java.util.List;

    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.AnnotationConfiguration;
    import org.hibernate.cfg.Configuration;

    import data.Employee;

    public class Client {

    public static void main(String[] args) {
    Configuration config = new AnnotationConfiguration().addAnnotatedClass(Employee.class).configure();

    SessionFactory sessionFactory = config.buildSessionFactory();
    Session session = sessionFactory.getCurrentSession();

    List result = null;
    try {
    session.beginTransaction();

    result = session.getNamedQuery("implicitSample").list();
    System.out.println("Result size : " + result.size());
    session.getTransaction().commit();
    } catch (Exception e) {
    e.printStackTrace();
    }
    System.out.println(result.size());
    }

    }
    Client.java
  4. Jar Files: The following jar files need to be included in the classpath
    hibernate3.jar
    commons-collections-2.1.1.jar
    antlr-2.7.6.jar
    commons-logging-1.0.4.jar
    hibernate-annotations.jar
    ejb3-persistence.jar
    hibernate-commons-annotations.jar
    dom4j-1.6.1.jar
    ojdbc14.jar
    jta.jar
    log4j-1.2.11.jar
    xerces-2.6.2.jar
    xml-apis.jar
    cglib-2.1.3.jar
    asm.jar
    All the Jar files will be available in the hibernate download. The hibernate-annotations.jar file is available in the hibernate annotations download.
This example was tested on Java 5 Update 9 with Hibernate version 3.2.3 and Hibernate Annotations Version: 3.3.0.GA.

Tuesday, April 03, 2007

Using JSON from Java

JSON (JavaScript Object Notation) is a lightweight data-interchange format based on a subset of the JavaScript Programming Language. JSON object structure is built on two structures:
  • A collection of name/value pairs: An associative array in Javascript which is analogous to the Java Map.
  • An ordered list of values: Analogous to a Java Array.
The rest of the datatypes (string, number, true/false etc.) have straight forward mappings to the Java datatypes.

Producing JSON from Java


Since JSON objects can be declared literally in JavaScript, it is possible to create these literals from Java classes. There are a couple of ways to achieve this
  • Using JSONObject: The source code for JSONObject and the supporting classes is available at http://www.json.org/java/index.html. Using this you can create JSONObject from a Java class as shown below
      JSONObject json = new JSONObject();
    json.put("name1",value1);
    json.put("name2",value2);
    The JSONObject class will appear as a Javascript Associate array which looks like this
     {"name1": "value1",
    "name2": "value2"}
  • Using JSON-lib: This is a Java library (available on sourceforge) that provides utility methods to convert different Java objects into JSONObject. This method is used in the example shown below.
The following Java class and JSP demonstrate how to use JSON with Java
  1. The Java class:
    package beans;

    import java.util.Map;
    import java.util.TreeMap;

    import net.sf.json.JSONObject;

    public class MapHolder {
    private Map map;

    public Map getMap() {
    return map;
    }

    public void setMap(Map map) {
    this.map = map;
    }

    public JSONObject getJsonObject() {
    JSONObject obj = JSONObject.fromMap(map);
    return obj;
    }

    public MapHolder() {
    map = new TreeMap();
    map.put("name1", "value1");
    map.put("name2", "value2");

    }
    }
    MapHolder.java
    • The getJsonObject() method uses JSON-lib to convert the Map to a JSONObject.
  2. The JSP:
    <%@page import="beans.MapHolder,net.sf.json.JSONObject"%>
    <html>
    <head>
    <title>Title</title>
    <script>
    function getInfo(o)
    {
    var obj=o;
    document.getElementById('test').innerHTML=obj.name1;
    }
    </script>
    </head>
    <body>
    <%
    JSONObject obj = (new MapHolder()).getJsonObject();
    %>
    <input type="button" value="submit" onclick='getInfo(<%=obj%>)' />

    <div id="test"></div>
    </body>
    </html>
    index.jsp
    • Note the "obj.name1" in the script to access the element from the associative array.

  3. The JAR files: In order to use JSON-lib, you will need to include the following jars in your classpath

Friday, March 30, 2007

Reverse Ajax with Direct Web Remoting (DWR)

Direct Web Remoting (DWR), is an open source Java library that can be used to implement Ajax in Java web applications with minimal Javascript coding. Using DWR, we can invoke server-side Java methods from Javascript in the browser. DWR 2.0 introduces a new feature, dubbed "Reverse Ajax", using which server-side Java can "push" updates to the browser. In this post, I tried to use a simplistic web application that will demonstrate the use of DWR for "Reverse Ajax".
In this example, I use a servlet that will be pushing information to the browser clients. Here is how to implement the example.
  1. Download DWR 2.0 from here, dwr.jar file has to be included in the classpath.
  2. Create the Service: This service generates messages which will be written to the browser. Here is the code for the Service.
    package utils;

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;

    import org.directwebremoting.ServerContext;
    import org.directwebremoting.proxy.dwr.Util;

    public class Service {

    private int count = 0;

    public void update(ServerContext wctx) {
    List<Data> messages = new ArrayList<Data>();
    messages.add(new Data("testing" + count++));

    // Collection sessions = wctx.getAllScriptSessions();
    Collection sessions = wctx.getScriptSessionsByPage("/ReverseAjax/index.html");
    Util utilAll = new Util(sessions);
    utilAll.addOptions("updates", messages, "value");
    }
    }
    Service.java
  3. Create the Message Container: The message container is a simple Java bean that holds the message.
    package utils;

    public class Data {
    private String value;

    public Data() {
    }

    public Data(String value) {
    this.value = value;
    }

    public String getValue() {
    return value;
    }

    public void setValue(String value) {
    this.value = value;
    }
    }
    Data.java
  4. Create the Servlet: Here is the code for the Servlet.
    package servlets;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.directwebremoting.ServerContext;
    import org.directwebremoting.ServerContextFactory;

    import utils.Service;

    public class TestServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    Service service = new Service();
    ServerContext wctx = ServerContextFactory.get(this.getServletContext());
    for (int i = 0; i < 10; i++) {
    service.update(wctx);
    try {
    Thread.sleep(1000);
    }
    catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    PrintWriter writer = response.getWriter();
    writer.println("Done");
    writer.close();

    }}
    TestServlet.java
    • The ServerContext is used by DWR to get information of the clients that have open sessions on the server.
  5. The Web Page: This is the code for the Web Page (index.html).
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>index</title>
    <script type='text/javascript' src='dwr/engine.js'></script>
    <script type='text/javascript' src='dwr/interface/Service.js'></script>
    <script type='text/javascript' src='dwr/util.js'></script>
    </head>
    <body onload="dwr.engine.setActiveReverseAjax(true);">
    <ul id="updates">
    </ul>

    </body>
    </html>
    index.html
    • engine.js handles all server communications.
    • util.js helps you alter web pages with the data you got from the server.
    • The path to the scripts is relative to the root of the web content. The DWR servlet (defined in the web.xml file) will provide these scripts.
    • dwr.engine.setActiveReverseAjax(true); is used to activate Reverse Ajax
    • The id of the list is the same as the parameter set in the Service.
  6. The Web Deployment Descriptor:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" 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">
    <display-name>ReverseAjax</display-name>

    <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
    <param-name>debug</param-name>
    <param-value>true</param-value>
    </init-param>
    <init-param>
    <param-name>activeReverseAjaxEnabled</param-name>
    <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>servlets.TestServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/testServlet</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    </web-app>
    WEB-INF/web.xml
    • The DWR Servlet has to be loaded on startup
    • Setting activeReverseAjaxEnabled to true sets Reverse Ajax to be active. In this case Reverse Ajax used will be through polling or comet requests (extended http requests). If this is false, then inactive Reverse Ajax (piggybacking) will be used. In this case, the server waits for requests from the client and piggybacks the updates with the response.
  7. The DWR Configuration: The DWR configuration is defined in the dwr.xml file
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr/dwr20.dtd">
    <dwr>
    <allow>
    <create creator="new" javascript="Service" scope="application">
    <param name="class" value="utils.Service" />
    </create>
    <convert converter="bean" match="utils.Data" />
    </allow>
    </dwr>
    WEB-INF/dwr.xml
    • "create" is used to define a Java object as being available to Javascript code.
    • The "javascript" attribute is the one that will be used in case of invoking the server-side methods from Javascript.
    • The converter definition allows utils.Data to be used as a parameter.

  8. Environment: This example was tested using DWR 2.0 RC 3, on Tomcat 5.5

Thursday, March 15, 2007

My First Guice Web Application

Google Guice is an open source Dependency Injection framework from Google. Guice is based on annotations and generics unlike Spring which depends on XML of Java for wiring dependencies. Guice injects constructors, fields and methods (any methods with any number of arguments, not just setters). Guice provides support for custom scopes, static member injection, Spring as well as Struts 2.x integration and AOP Alliance method interception. Guice is available at Google Code. You can find the User guide and Javadocs from there. This post describes how I implemented a simple Web application using Guice. For injecting dependencies into Servlets, I used a Listener (this idea was from Hani Suleiman in the Guice developer mailing list). Follow these steps to run the example.
  1. Download Guice from the Guice Site.
  2. Create the Service Interface and Implementation: These are fairly simple as shown below
    package services;

    public interface Service {
    public String doWork();
    }
    Service.java
    package services;

    public class ServiceImpl implements Service {

    private int count = 0;
    public String doWork() {
    count++;
    System.out.println("Service Impl Doing work " + count);
    return("ServiceImpl : " + count);
    }
    }
    ServiceImpl.java
  3. Create an Annotation: Guice allows you to select which implementation of an Interface you want to inject, by using annotations. You will see this used in the TestServlet. Here is the annotation I defined
    package annotations;

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    import com.google.inject.BindingAnnotation;

    @Retention(RetentionPolicy.RUNTIME)
    @Target( { ElementType.FIELD, ElementType.PARAMETER })
    @BindingAnnotation
    public @interface MyAnnotation {

    }
    MyAnnotation.java
    • @Rentention(RUNTIME) makes the annotation visible at runtime.(this is mandatory when using Guice)
    • @Target({FIELD, PARAMETER}) prevents the annotation from being applied to methods, types, local variables, and other annotations.(This is optional)
    • @BindingAnnotation is a Guice-specific signal that you wish your annotation to be used in this way. Guice will produce an error whenever user applies more than one binding annotation to the same injectable element. (This is necessary when used for binding with Guice)
    • Optionally, you can also use annotations with attributes to differentiate between bindings.
  4. Create a Guice Module: Guice Modules are used to define bindings within an applications. It is possible to have multiple modules per application (and multiple applications can share a common module too). Here is the simple Module that I implemented.
    package modules;

    import services.Service;
    import services.ServiceImpl;
    import annotations.MyAnnotation;

    import com.google.inject.AbstractModule;
    import com.google.inject.Scopes;

    public class MyModule extends AbstractModule {

    public void configure() {
    bind(Service.class).annotatedWith(MyAnnotation.class).to(ServiceImpl.class).in(Scopes.SINGLETON);
    }

    }
    MyModule.java
    • Guice provides the module with a binder which is used to bind the Interfaces with implementations.
    • annotatedWith(MyAnnotation.class) defines the binding to the implementation based on the annotation.
    • in(Scopes.SINGLETON) is used to define this implementation as a Singleton
  5. Create a Listener: Here is the Code for a simple listener (for a more complete example see the above mentioned mailing list).
    package listeners;

    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;

    import modules.MyModule;

    import com.google.inject.Guice;
    import com.google.inject.Injector;
    import com.google.inject.Module;
    import com.google.inject.servlet.ServletModule;

    public class GuiceServletContextListener implements ServletContextListener {

    public void contextDestroyed(ServletContextEvent servletContextEvent) {
    ServletContext servletContext = servletContextEvent.getServletContext();
    servletContext.removeAttribute(Injector.class.getName());
    }

    public void contextInitialized(ServletContextEvent servletContextEvent) {
    Injector injector = Guice.createInjector(new Module[] { new MyModule(), new ServletModule() });
    ServletContext servletContext = servletContextEvent.getServletContext();
    servletContext.setAttribute(Injector.class.getName(), injector);
    }
    }
    GuiceServletContextListener.java
    • The listener is used to save the Injector in the servlet context to be used in the AbstractInjectableServlet.
    • An injector is to be built during startup and used to inject objects at runtime.
    • ServletModule is used for servlet-specific bindings. It adds a request/session scope, as well as allowing the injection of request,response, and request parameters into your beans. To use it you have to register GuiceFilter in your web.xml.
  6. Create an Abstract Servlet: The AbstractInjectableServlet will be super class of all the Servlets you use. This class uses the injector to inject dependencies in the concrete classes by the injector.injectMembers(this) method.
    package servlets;

    import javax.servlet.ServletConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;

    import com.google.inject.Injector;

    public abstract class AbstractInjectableServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException{

    ServletContext context = config.getServletContext();
    Injector injector = (Injector) context.getAttribute(Injector.class.getName());
    if (injector == null) {
    throw new ServletException("Guice Injector not found");
    }
    injector.injectMembers(this);

    }
    }
    AbstractInjectableServlet.java
  7. Create the Test Servlet: Here is the code for the TestServlet
    package servlets;

    import java.io.IOException;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import services.Service;
    import annotations.MyAnnotation;

    import com.google.inject.Inject;


    public class TestServlet extends AbstractInjectableServlet implements javax.servlet.Servlet {

    private Service service;
    public TestServlet() {
    super();
    }

    @Inject
    public void setServiceInstance(@MyAnnotation Service service) {
    this.service = service;
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.getWriter().println("Service instance : " + service.doWork());
    }
    }
    TestServlet.java
    • The @Inject is used to define methods to be injectable.
    • The @MyAnnotation for the Service parameter means that, the actual binding will be done with the implementation that is bound using the withAnnotation() method.
  8. The Web Deployment Descriptor
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4"
    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">
    <display-name>GuiceWeb</display-name>
    <listener>
    <listener-class>listeners.GuiceServletContextListener</listener-class>
    </listener>
    <servlet>
    <description></description>
    <display-name>TestServlet</display-name>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>servlets.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/testServlet</url-pattern>

    </servlet-mapping>

    <welcome-file-list>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    </web-app>
    web.xml

Monday, March 12, 2007

Message Driven Bean in Java EE 5 : Part 2

In a previous post, I described how to implement Messaging in Java EE 5 using annotation. This post is an extension of that post to describe how to implement Messaging in Java EE 5 using a deployment descriptor. To implement this, you can use the same client that was described in the other post. The only change needed is in the MDB part. Here's how to implement a Message Driven bean using the deployment descriptor.
  1. Create the Message driven bean: The message driven bean here is a simple Java class that implements the MessageListener interface. All the configuration will be done in the deployment descriptors.
    package jms;

    import javax.jms.JMSException;
    import javax.jms.Message;
    import javax.jms.MessageListener;
    import javax.jms.TextMessage;

    import org.apache.log4j.Level;
    import org.apache.log4j.Logger;

    public class Messaging3Mdb implements MessageListener {

    static final Logger logger = Logger.getLogger("MDB");

    public Messaging3Mdb() {
    }

    public void onMessage(Message inMessage) {
    TextMessage msg = null;
    logger.setLevel(Level.ALL);

    try {
    if (inMessage instanceof TextMessage) {
    msg = (TextMessage) inMessage;
    logger.info("MESSAGE BEAN: Message received: " + msg.getText());
    } else {
    logger.warn("Message of wrong type: " + inMessage.getClass().getName());
    }
    } catch (JMSException e) {
    logger.error("MessageBean.onMessage: JMSException: " + e.toString());
    } catch (Throwable te) {
    logger.error("MessageBean.onMessage: Exception: " + te.toString());
    }
    }
    }
    Messaging3Mdb.java
  2. The sun-ejb-jar.xml file: This file contains the mapping of the JMS resources.
    <?xml version="1.0" encoding="UTF-8"?>
    <sun-ejb-jar>
    <enterprise-beans>
    <name>Ejb3DD</name>
    <ejb>
    <ejb-name>Messaging3Mdb</ejb-name>
    <jndi-name>jms/testQueue</jndi-name>
    <mdb-connection-factory>
    <jndi-name>jms/connectionFactory</jndi-name>
    </mdb-connection-factory>
    </ejb>
    </enterprise-beans>
    </sun-ejb-jar>
    META-INF/sun-ejb-jar.xml
  • The EJB deployment descriptor:
    <?xml version="1.0" encoding="UTF-8" ?>
    <ejb-jar>
    <enterprise-beans>
    <message-driven>
    <ejb-name>Messaging3Mdb</ejb-name>
    <ejb-class>jms.Messaging3Mdb</ejb-class>
    <messaging-type>javax.jms.MessageListener</messaging-type>
    <message-destination-type>javax.jms.Queue</message-destination-type>

    </message-driven>
    </enterprise-beans>
    </ejb-jar>
    META-INF/ejb-jar.xml

  • Environment: This example was implemented on Glassfish v1, Milestone 7
  • Friday, March 09, 2007

    Securing Middle tier Objects with Acegi Security Framework

    Previously, I posted an example on implementing Security using Acegi Security Framework for applications using Spring framework. This post will describe an example on how to secure Middle-tier objects using Acegi, with Role-based authorization. Here is how to implement the example.
    1. Create the example project as shown in "Spring security with Acegi Security Framework". This will be the starting point.
    2. Create the Secure Object: The secure object here is SecureDAO, which is a dummy object that has the common create, read, update, delete methods.
      package test;

      public class SecureDAO {
      public String create() {
      return "create";
      }

      public String read() {
      return "read";
      }

      public String update() {
      return "update";
      }

      public String delete() {
      return "delete";
      }
      }
      SecureDAO.java
    3. Create the Test Servlet: The servlet is used to invoke the secure DAO object.
      package servlets;

      import java.io.IOException;

      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;

      import org.springframework.web.context.WebApplicationContext;
      import org.springframework.web.context.support.WebApplicationContextUtils;

      import test.SecureDAO;

      public class TestServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

      public TestServlet() {
      super();
      }

      protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      String method = request.getParameter("method");

      WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());

      SecureDAO obj = (SecureDAO) context.getBean("secureDAO");
      String result = "";
      if (method.equals("create")) {
      result = obj.create();
      }
      if (method.equals("read")) {
      result = obj.read();
      }

      if (method.equals("update")) {
      result = obj.update();
      }

      if (method.equals("delete")) {
      result = obj.delete();
      }

      response.getWriter().println(result);

      }
      }
      TestServlet.java
    4. Update the authenticatedusers.jsp file to invoke the TestSerlvet, as shown below
      <%@ page import="org.acegisecurity.context.SecurityContextHolder"%>
      <h1>Welcome: <%=SecurityContextHolder.getContext().getAuthentication().getName()%></h1>
      <p><a href="../">Home</a>
      <form action="/SpringSecurity/TestServlet"><select name="method">
      <option value="create">create</option>
      <option value="read">read</option>
      <option value="update">update</option>
      <option value="delete">delete</option>
      </select> <input type="submit" name="submit" /></form>
      <p><a href="../j_acegi_logout">Logout</a>
      authenticatedusers.jsp

    5. Update the applicationContext.xml file to include the security definitions by adding the following bean definitions as shown below
      <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
      <property name="authenticationManager">
      <ref bean="authenticationManager" />
      </property>
      <property name="accessDecisionManager">
      <bean class="org.acegisecurity.vote.AffirmativeBased">
      <property name="allowIfAllAbstainDecisions" value="false" />
      <property name="decisionVoters">
      <list>
      <bean class="org.acegisecurity.vote.RoleVoter" />
      <bean class="org.acegisecurity.vote.AuthenticatedVoter" />
      </list>
      </property>
      </bean>
      </property>
      <property name="objectDefinitionSource">
      <value>
      test.SecureDAO.*=IS_AUTHENTICATED_REMEMBERED
      test.SecureDAO.delete=ROLE_ADMIN
      </value>
      </property>
      </bean>


      <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
      <property name="interceptorNames">
      <list>
      <value>methodSecurityInterceptor</value>
      </list>
      </property>
      <property name="beanNames">
      <list>
      <value>secureDAO</value>
      </list>
      </property>
      </bean>

      <bean id="secureDAO" class="test.SecureDAO" />
      Note
      • The MethodSecurityInterceptor is used to define the mapping between the Methods and the roles that are allowed to invoke these methods.
      • The BeanNameAutoProxyCreator is used to create a proxy for the secureDAO object, so that an authorization check may be applied to every invocation on the object.
    6. Add the cglib JAR file available in the Spring download as a dependency to your project. This is used for creating the Proxy.

    Tuesday, March 06, 2007

    Charts with JFreeChart 1

    JFreeChart is a free Java chart library that can be used to display charts from Java applications. It features:
    • A wide range of chart types
    • support for many output types, including Swing components, image files (including PNG and JPEG), and vector graphics file formats (including PDF, EPS and SVG);
    • It is distributed under the terms of the LGPL, which permits use in proprietary applications.
    In this post, I will describe how to use JFreeChart to display a comparative barchart, with data that contains multiple series which have to be grouped (G1, G2) per date. This effectively gives three levels of information (series, group and date). In order to display this data in a chart, we can use a StackedBarChart from JFreeChart. Please note that this is a result of my playing around with JFreeChart, and is not from the official documentation, which means that some of my assumptions may not be accurate. Follow these steps to run the example
    1. Download JFreeChart from here.
    2. Create the DataSet: The StakedBarChart takes a CategoryDataset as input. The following piece of code shows how to add data to the CategoryDataset.
        DefaultCategoryDataset dataSet = new DefaultCategoryDataset();

      dataSet.addValue(100, "Series1 G1", "Jan 04");
      dataSet.addValue(150, "Series1 G2", "Jan 04");
      It is important to note that the order in which you add data to the Dataset, as this would affect how you assign colors to the different series.
    3. Create the renderer and assign series colors:
        GroupedStackedBarRenderer renderer = new GroupedStackedBarRenderer();
      renderer.setItemMargin(0.01);
      renderer.setDrawBarOutline(false);

      renderer.setSeriesPaint(0, Color.blue);
      renderer.setSeriesPaint(1, Color.blue);
      The order of the series in the Dataset, will dictate series number when assigning colors. You will see this in the full code shown below.
    4. Map series to groups: This is done using the KeyToGroupMap.
        KeyToGroupMap keytogroupmap = new KeyToGroupMap("G1");
      keytogroupmap.mapKeyToGroup("Series1 G1", "G1");
      keytogroupmap.mapKeyToGroup("Series2 G1", "G1");
    5. Create the Legend: Since I wanted to give same color to a given series in different groups, I had to add a legend collection that maps colors to the series
       private static LegendItemCollection createLegendItems() {
      LegendItemCollection legenditemcollection = new LegendItemCollection();
      LegendItem legenditem = new LegendItem("Series1", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.blue);
      LegendItem legenditem1 = new LegendItem("Series2", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.cyan);
      LegendItem legenditem2 = new LegendItem("Series3", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.green);
      LegendItem legenditem3 = new LegendItem("Series4", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.yellow);
      legenditemcollection.add(legenditem);
      legenditemcollection.add(legenditem1);
      legenditemcollection.add(legenditem2);
      legenditemcollection.add(legenditem3);
      return legenditemcollection;
      }
    6. The code
      package chart;

      import java.awt.Color;

      import org.jfree.chart.ChartFactory;
      import org.jfree.chart.ChartFrame;
      import org.jfree.chart.JFreeChart;
      import org.jfree.chart.LegendItem;
      import org.jfree.chart.LegendItemCollection;
      import org.jfree.chart.axis.SubCategoryAxis;
      import org.jfree.chart.plot.CategoryPlot;
      import org.jfree.chart.plot.Plot;
      import org.jfree.chart.plot.PlotOrientation;
      import org.jfree.chart.renderer.category.GroupedStackedBarRenderer;
      import org.jfree.data.KeyToGroupMap;
      import org.jfree.data.category.CategoryDataset;
      import org.jfree.data.category.DefaultCategoryDataset;

      public class StackedBarChartTest {

      private static CategoryDataset getData() {
      DefaultCategoryDataset dataSet = new DefaultCategoryDataset();

      dataSet.addValue(100, "Series1 G1", "Jan 04");
      dataSet.addValue(150, "Series1 G2", "Jan 04");

      dataSet.addValue(60, "Series2 G1", "Feb 04");
      dataSet.addValue(90, "Series2 G2", "Feb 04");

      dataSet.addValue(70, "Series3 G2", "Feb 04");
      dataSet.addValue(99, "Series3 G1", "Feb 04");

      dataSet.addValue(100, "Series4 G1", "Feb 04");
      dataSet.addValue(80, "Series4 G2", "Feb 04");

      dataSet.addValue(20, "Series1 G1", "Feb 04");
      dataSet.addValue(50, "Series1 G2", "Feb 04");

      dataSet.addValue(10, "Series2 G1", "Jan 04");
      dataSet.addValue(40, "Series2 G2", "Jan 04");

      dataSet.addValue(10, "Series3 G2", "Jan 04");
      dataSet.addValue(50, "Series3 G1", "Jan 04");

      dataSet.addValue(10, "Series4 G1", "Jan 04");
      dataSet.addValue(60, "Series4 G2", "Jan 04");

      // dataSet.addValue(100, "Series1 G1", "Jan 04");
      // dataSet.addValue(20, "Series1 G1", "Feb 04");
      // dataSet.addValue(10, "Series2 G1", "Jan 04");
      // dataSet.addValue(60, "Series2 G1", "Feb 04");
      // dataSet.addValue(99, "Series3 G1", "Feb 04");
      // dataSet.addValue(50, "Series3 G1", "Jan 04");
      // dataSet.addValue(100, "Series4 G1", "Feb 04");
      // dataSet.addValue(10, "Series4 G1", "Jan 04");
      //
      //
      // dataSet.addValue(150, "Series1 G2", "Jan 04");
      // dataSet.addValue(50, "Series1 G2", "Feb 04");
      // dataSet.addValue(40, "Series2 G2", "Jan 04");
      // dataSet.addValue(90, "Series2 G2", "Feb 04");
      // dataSet.addValue(10, "Series3 G2", "Jan 04");
      // dataSet.addValue(70, "Series3 G2", "Feb 04");
      // dataSet.addValue(60, "Series4 G2", "Jan 04");
      // dataSet.addValue(80, "Series4 G2", "Feb 04");

      return dataSet;
      }

      private static JFreeChart createChart(CategoryDataset categorydataset) {
      JFreeChart jfreechart = ChartFactory.createStackedBarChart("Sample", null, "value", categorydataset, PlotOrientation.VERTICAL, true, true, false);
      GroupedStackedBarRenderer renderer = new GroupedStackedBarRenderer();
      renderer.setItemMargin(0.01);
      renderer.setDrawBarOutline(false);

      renderer.setSeriesPaint(0, Color.blue);
      renderer.setSeriesPaint(1, Color.blue);

      renderer.setSeriesPaint(2, Color.cyan);
      renderer.setSeriesPaint(3, Color.cyan);

      renderer.setSeriesPaint(4, Color.green);
      renderer.setSeriesPaint(5, Color.green);

      renderer.setSeriesPaint(6, Color.yellow);
      renderer.setSeriesPaint(7, Color.yellow);

      // renderer.setSeriesPaint(0, Color.blue);
      // renderer.setSeriesPaint(4, Color.blue);
      //
      // renderer.setSeriesPaint(1, Color.cyan);
      // renderer.setSeriesPaint(5, Color.cyan);
      //
      // renderer.setSeriesPaint(2, Color.green);
      // renderer.setSeriesPaint(6, Color.green);
      //
      // renderer.setSeriesPaint(3, Color.yellow);
      // renderer.setSeriesPaint(7, Color.yellow);

      KeyToGroupMap keytogroupmap = new KeyToGroupMap("G1");
      keytogroupmap.mapKeyToGroup("Series1 G1", "G1");
      keytogroupmap.mapKeyToGroup("Series2 G1", "G1");
      keytogroupmap.mapKeyToGroup("Series3 G1", "G1");
      keytogroupmap.mapKeyToGroup("Series4 G1", "G1");

      keytogroupmap.mapKeyToGroup("Series1 G2", "G2");
      keytogroupmap.mapKeyToGroup("Series2 G2", "G2");
      keytogroupmap.mapKeyToGroup("Series3 G2", "G2");
      keytogroupmap.mapKeyToGroup("Series4 G2", "G2");

      renderer.setSeriesToGroupMap(keytogroupmap);
      SubCategoryAxis subcategoryaxis = new SubCategoryAxis("Series/Month");
      subcategoryaxis.setCategoryMargin(0.03);
      subcategoryaxis.addSubCategory("G1");
      subcategoryaxis.addSubCategory("G2");

      CategoryPlot categoryplot = (CategoryPlot) jfreechart.getPlot();
      categoryplot.setDomainAxis(subcategoryaxis);
      categoryplot.setRenderer(renderer);
      categoryplot.setFixedLegendItems(createLegendItems());

      return jfreechart;
      }

      private static LegendItemCollection createLegendItems() {
      LegendItemCollection legenditemcollection = new LegendItemCollection();
      LegendItem legenditem = new LegendItem("Series1", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.blue);
      LegendItem legenditem1 = new LegendItem("Series2", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.cyan);
      LegendItem legenditem2 = new LegendItem("Series3", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.green);
      LegendItem legenditem3 = new LegendItem("Series4", "-", null, null, Plot.DEFAULT_LEGEND_ITEM_BOX, Color.yellow);
      legenditemcollection.add(legenditem);
      legenditemcollection.add(legenditem1);
      legenditemcollection.add(legenditem2);
      legenditemcollection.add(legenditem3);
      return legenditemcollection;
      }

      public static void main(String args[]) {

      StackedBarChartTest demo = new StackedBarChartTest();

      ChartFrame frame = new ChartFrame("First", demo.createChart(demo.getData()));
      frame.pack();
      frame.setVisible(true);
      }
      }
      Note:
      • The SubCategoryAxis is used to assign labels to Category Axis' subcategories (i.e. groups).
      • The ChartFrame is used to display the results. The results could also be written to an output stream using the ChartUtilities.writeChartAsJPEG or ChartUtilities.writeChartAsPNG methods.
      • The commented lines show how the series color is affected by the order in which the data is entered into the Dataset.

    Wednesday, February 28, 2007

    Java EE best practices

    A recent article in IBM developerworks discusses best practices in Java EE development. This is an update of the 2004 article on J2EE best practices. Here's the complete list:
    1. Always use MVC.
    2. Don't reinvent the wheel.
    3. Apply automated unit tests and test harnesses at every layer.
    4. Develop to the specifications, not the application server.
    5. Plan for using Java EE security from Day One.
    6. Build what you know.
    7. Always use session facades whenever you use EJB components.
    8. Use stateless session beans instead of stateful session beans.
    9. Use container-managed transactions.
    10. Prefer JSPs as your first choice of presentation technology.
    11. When using HttpSessions, store only as much state as you need for the current business transaction and no more.
    12. Take advantage of application server features that do not require your code to be modified.
    13. Play nice within existing environments.
    14. Embrace the qualities of service provided by the application server environment.
    15. Embrace Java EE, don't fake it.
    16. Plan for version updates.
    17. At all points of interest in your code, log your program state using a standard logging framework.
    18. Always clean up after yourself.
    19. Follow rigorous procedures for development and testing.
    Links
    1. Top Java EE Best Practices
    2. The top 10 (more or less) J2EE best practices

    Monday, February 26, 2007

    Custom Search Update

    Back from the vacation. Updated the Java custom search - Now includes over 100 Java related sites.

    Friday, February 02, 2007

    Google Related links

    Added a related links control on the top. Let's see if we get any use out of this.

    This will be the last post for a couple of weeks, as I will be on vacation in India.

    Monday, January 29, 2007

    Integrating Struts 2.0 with Spring

    In the past, I posted an example on how to use Displaytag with Struts and Spring, using Spring JDBC for data access(1, 2). In this post, I will describe how to do the same using Struts 2.0. The only major step that needs to be done here is to override the default Struts 2.0 OjbectFactory. Changing the ObjectFactory to Spring give control to Spring framework to instantiate action instances etc. Most of the code is from the previous post, but I will list only the additional changes here.
    1. Changing the default Object factory: In order to change the Ojbect factory to Spring, you have to add a declaration in the struts.properties file.
      struts.objectFactory = spring
      struts.devMode = true
      struts.enable.DynamicMethodInvocation = false
      src/struts.properties
    2. The Action class: Here is the code for the action class
      package actions;

      import java.util.List;

      import business.BusinessInterface;

      import com.opensymphony.xwork2.ActionSupport;

      public class SearchAction extends ActionSupport {
      private BusinessInterface businessInterface;

      private String minSalary;

      private String submit;

      private List data;

      public String getSubmit() {
      return submit;
      }

      public void setSubmit(String submit) {
      this.submit = submit;
      }

      public BusinessInterface getBusinessInterface() {
      return businessInterface;
      }

      public String execute() throws Exception {
      try {
      long minSal = Long.parseLong(getMinSalary());
      System.out.println("Business Interface: " + businessInterface + "Minimum salary : " + minSal);
      data = businessInterface.getData(minSal);
      System.out.println("Data : " + data);

      } catch (Exception e) {
      e.printStackTrace();
      }

      return SUCCESS;
      }

      public void setBusinessInterface(BusinessInterface bi) {
      businessInterface = bi;
      }

      public String getMinSalary() {
      return minSalary;
      }

      public void setMinSalary(String minSalary) {
      this.minSalary = minSalary;
      }

      public List getData() {
      return data;
      }

      public void setData(List data) {
      this.data = data;
      }
      }
      SearchAction.java
      • The Action class here does not have access to the HttpServetRequest and HttpServletResponse. Hence the action class itself was changed to the session scope for this example (see below)
      • In order for the action class to be aware of the Http Session, the action class has to implement the ServletRequestAware interface, and define a setServletRequest method, which will be used to inject the ServletRequest into the action class.
      • The BusinessInterface property is injected by Spring framework.
    3. The struts Configuration:
      <!DOCTYPE struts PUBLIC
      "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
      "http://struts.apache.org/dtds/struts-2.0.dtd">
      <struts>
      <package name="Struts2Spring" namespace="/actions" extends="struts-default">
      <action name="search" class="actions.SearchAction">
      <result>/search.jsp</result>
      </action>
      </package>
      </struts>
      src/struts.xml
      • The action's class attribute has to map the id attribute of the bean defined in the spring bean factory definition.
    4. The Spring bean factory definition
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
      default-autowire="autodetect">
      <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
      <property name="driverClassName">
      <value>oracle.jdbc.driver.OracleDriver</value>
      </property>
      <property name="url">
      <value>jdbc:oracle:thin:@localhost:1521:orcl</value>
      </property>
      <property name="username">
      <value>scott</value>
      </property>
      <property name="password">
      <value>tiger</value>
      </property>
      </bean>

      <!-- Configure DAO -->
      <bean id="empDao" class="data.DAO">
      <property name="dataSource">
      <ref bean="dataSource"></ref>
      </property>
      </bean>
      <!-- Configure Business Service -->
      <bean id="businessInterface" class="business.BusinessInterface">
      <property name="dao">
      <ref bean="empDao"></ref>
      </property>
      </bean>
      <bean id="actions.SearchAction" name="search" class="actions.SearchAction" scope="session">
      <property name="businessInterface" ref="businessInterface" />
      </bean>
      </beans>
      WEB-INF/applicationContext.xml
      • The bean definition for the action class contains the id attribute which matches the class attribute of the action in struts.xml
      • Spring 2's bean scope feature can be used to scope an Action instance to the session, application, or a custom scope, providing advanced customization above the default per-request scoping.

    5. The web deployment descriptor
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app id="WebApp_9" version="2.4" 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">

      <display-name>Struts2Spring</display-name>

      <filter>
      <filter-name>struts2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
      </filter>

      <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
      </filter-mapping>

      <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <listener>
      <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
      </listener>
      <welcome-file-list>
      <welcome-file>index.html</welcome-file>
      </welcome-file-list>
      </web-app>
      web.xml
      The only significant addition here is that of the RequestContextListener. This listener allows Spring framework, access to the HTTP session information.
    6. The JSP file: The JSP file is shown below. The only change here is that the action class, instead of the Data list is accessed from the session.
      <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
      <%@ taglib uri="http://displaytag.sf.net" prefix="display"%>
      <%@ taglib prefix="s" uri="/struts-tags"%>
      <%@ page import="actions.SearchAction,beans.Employee,business.Sorter,java.util.List,org.displaytag.tags.TableTagParameters,org.displaytag.util.ParamEncoder"%>
      <html>
      <head>
      <title>Search page</title>
      <link rel="stylesheet" type="text/css" href="/StrutsPaging/css/screen.css" />
      </head>
      <body bgcolor="white">
      <s:form action="/actions/search.action">
      <table>
      <tr>
      <td>Minimum Salary:</td>
      <td><s:textfield label="minSalary" name="minSalary" /></td>
      </tr>
      <tr>
      <td colspan="2"><s:submit name="submit" /></td>
      </tr>
      </table>
      </s:form>
      <jsp:scriptlet>

      SearchAction action = (SearchAction)session.getAttribute("actions.SearchAction");
      session.setAttribute("empList", action.getData());
      if (session.getAttribute("empList") != null) {
      String sortBy = request.getParameter((new ParamEncoder("empTable")).encodeParameterName(TableTagParameters.PARAMETER_SORT));
      Sorter.sort((List) session.getAttribute("empList"), sortBy);

      </jsp:scriptlet>

      <display:table name="sessionScope.empList" pagesize="4" id="empTable" sort="external" defaultsort="1" defaultorder="ascending" requestURI="">
      <display:column property="empId" title="ID" sortable="true" sortName="empId" headerClass="sortable" />
      <display:column property="empName" title="Name" sortName="empName" sortable="true" headerClass="sortable" />
      <display:column property="empJob" title="Job" sortable="true" sortName="empJob" headerClass="sortable" />
      <display:column property="empSal" title="Salary" sortable="true" headerClass="sortable" sortName="empSal" />
      </display:table>
      <jsp:scriptlet>
      }
      </jsp:scriptlet>

      </body>
      </html:html>
      search.jsp
    7. The Other required classes: The following other classes have been used for the example, and they can be obtained from the previous posts (1, 2).
      • Employee.java
      • BusinessInterface.java
      • Sorter.java
      • DAO.java
      • EmpMapper.java

    Tuesday, January 23, 2007

    Spring: Prototype beans with Singletons - 2

    The previous post described how to implement Lookup method injection and ServiceLocatorFactoryBean and BeanFactoryAware to enable singleton classes to use Prototype beans. This post will describe how to use Scoped beans (session or request or globalSession) from Spring managed beans in a Web application and also give the client servlet for testing the previous and the current examples.

    Using in Web Applications

    This is not for Singleton or Prototype beans. This solution is for using beans that are saved in the request of session scope. To implement this solution, you must inject an AOP proxy in place of the scoped bean, the proxy will then delegate the method call to the object that is in currently in the specified scope. All you have to do is to put the following in the bean definition
    <aop:scoped-proxy/>
    In the Web deployment descriptor (Shown in a following section), you will have to put a RequestContextListener in addition to the ContextLoaderListener. The RequestContextListener binds the HTTP request object to the Thread that is servicing that request. This makes beans that are request and session-scoped available further down the call chain (i.e. for spring managed beans invoked in the same thread of execution). For the Web Application scenario, I used a different Command bean shown below
    package beans;

    public class NewCommand {
    private String someProperty;

    public String getSomeProperty() {
    return someProperty;
    }

    public void setSomeProperty(String someProperty) {
    this.someProperty = someProperty;
    }

    public String execute() {
    return "Command property : " + someProperty;
    }
    }
    NewCommand.java

    The Command Manager is shown below
    package beans;

    public class CmdMgrWeb {
    private NewCommand command;

    public Object process(String prop) {
    command.setSomeProperty(prop);
    return command.execute();
    }

    public NewCommand getCommand() {
    return command;
    }

    public void setCommand(NewCommand command) {
    this.command = command;
    }
    }
    CmdMgrWeb.java

    The Servlet

    The following is the servlet used for testing the beans used here.
    package servlets;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.WebApplicationContextUtils;

    import beans.CmdMgrNoBFA;
    import beans.CmdMgrServiceFactory;
    import beans.CmdMgrWeb;
    import beans.CommandManager;
    import beans.NewCommand;

    public class MethodInjectionServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
    public MethodInjectionServlet() {
    super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
    request.getSession().setAttribute("command", new NewCommand());
    BeanFactory factory = (BeanFactory) context;
    PrintWriter writer = response.getWriter();

    CommandManager commandManager = (CommandManager) factory.getBean("commandManager");
    writer.println(commandManager.process("test"));

    CmdMgrNoBFA cmdMgrNoBFA = (CmdMgrNoBFA) factory.getBean("cmdMgrNoBFA");
    writer.println(cmdMgrNoBFA.process("test"));

    CmdMgrServiceFactory cmdMgrServiceFactory = (CmdMgrServiceFactory) factory.getBean("cmdMgrServiceFactory");
    writer.println(cmdMgrServiceFactory.process("test"));

    CmdMgrWeb cmdMgrWeb = (CmdMgrWeb) factory.getBean("cmdMgrWeb");
    writer.println(cmdMgrWeb.process("test"));

    }

    }
    MethodInjectionServlet.java

    The Web Deployment Descriptor

    The web.xml file is shown below. You can see the two listeners required for using spring and the scoped beans.
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4" 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">
    <display-name>MethodInjectionWeb</display-name>

    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

    <servlet>
    <description></description>
    <display-name>MethodInjectionServlet</display-name>
    <servlet-name>MethodInjectionServlet</servlet-name>
    <servlet-class>servlets.MethodInjectionServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>MethodInjectionServlet</servlet-name>
    <url-pattern>/MethodInjectionServlet</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    </web-app>
    WEB-INF/web.xml

    The Bean Definitions

    The following is the code for the applicationContext.xml file. It is quite self explanatory.
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

    <bean id="command" class="beans.Command" scope="prototype"></bean>

    <!-- Basic implementation by extending BeanFactoryAware -->
    <bean id="commandManager" class="beans.CommandManager"></bean>

    <!-- The lookup-method is implemented by Spring -->
    <bean id="cmdMgrNoBFA" class="beans.CmdMgrNoBFA">
    <lookup-method name="createCommand" bean="command" />
    </bean>

    <!-- Use Service locator factory to obtain a reference -->
    <bean id="springServiceFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
    <property name="serviceLocatorInterface" value="beans.ServiceFactory" />
    </bean>

    <bean id="cmdMgrServiceFactory" class="beans.CmdMgrServiceFactory">
    <property name="serviceFactory" ref="springServiceFactory"></property>
    </bean>


    <!-- Using in Web Applications -->
    <bean id="newCommand" class="beans.NewCommand" scope="session">
    <aop:scoped-proxy />
    </bean>

    <bean id="cmdMgrWeb" class="beans.CmdMgrWeb">
    <property name="command" ref="newCommand" />
    </bean>
    </beans>
    WEB-INF/applicationContext.xml

    Popular Posts