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.

    Popular Posts