Tuesday, July 8, 2008

GWT with JFreeChart in JBossPortal

While I was trying to search some useful links for GWT and JBossPortal together, I hardly found very little useful links on net. One is from ddwiki and another from Xantorohara. I must thank Xantorohara for his samples, those helped me a lot. Please visit Xantorohara's blog to start with the great combination of GWT and JBossPortal.

At first, Please download latest version of GWT JFreeChart and JBossPortal. I faced some problem with JBossPortal 2.6, latest Production version is 2.6.5, you can download it from here. Please, download the package JBoss Portal + JBoss AS 4.2.2.

And it will be great, if some one let me know the result of running the project in JBoss Portal 2.7.0 + JBoss AS 4.2.2. This is an alpha version of JBoss Portal.

I have developed the project in eclipse with Cypal plugins. Cypal is simply great. Please download this plugins if you are planning to start GWT in side eclipse.

Ok, Now that we have eclipse,Cypal,JFreeChart,GWT,JBoss Portal with JBoss AS, we can start our mission.

You can download the sample project now.

Let us look at directory structure first, it is important because we are mixing GWT and JBossPortal together.


FreeChart
|-src
|   |-client (gwt will convert to javascript)
|   |-public (contains pages,css,images,js files)
|   |-server (contains all server side runnable classes)
|   |-Chart.gwt.xml
|   |-ChartPortlet.java (our jbossportal specific class)
|
|-WebContent
     |-lib
     |-chart-object.xml (jbossportal specific descriptor file)
     |-portlet-instances.xml (jbossportal specific descriptor)
     |-portlet.xml (jsr 168 standard portlet descriptor)
     |-web.xml (web-app descriptor)



so, we are done with our basic structure.

If you are creating a new project, please follow Cypal's instruction. Or you can simply create your project using applicationCreator and projectCreator, which are bundled inside the GWT package. These are nice tools by GWT.

now let us see inside of classes and files.
First, Chart.html


<html>
<head>
</head>
<body>
<title>Wrapper HTML for Chart</title>
<script type="text/javascript" language="javascript"
       src="com.secl.chart.test.Chart.nocache.js">
</script>
<div
  id="bar">
</div>
</body>
</html>


Let us look at Chart.java



package com.secl.chart.test.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
import com.secl.chart.test.client.rpc.ChartService;

public class Chart implements EntryPoint
{
  private Image image;
  private class CallBack implements AsyncCallback
  {
     public void onFailure(Throwable caught)
     {
     }

     public void onSuccess(Object result)
     {
        String chartName = (String)result;
        String imageUrl = GWT.getModuleBaseURL() +
           "displayChart?filename=" +
           chartName;
        image.setUrl(imageUrl);
     }
  }

  private CallBack callBack = new CallBack();

  public void onModuleLoad()
  {
     image = new Image();
     ChartService.Util.getInstance().generateChart( callBack);
     String slotId =
        getEmptySlotId(RootPanel.getBodyElement(),"bar");
     RootPanel rootPanel = RootPanel.get(slotId);
     if(rootPanel != null)
     {
        rootPanel.add(image);
     }
  }

  private String getEmptySlotId(Element element, String idPrefix)
  {
     int count = DOM.getChildCount(element);
     if (count == 0)
     {
        String id = DOM.getElementAttribute(element, "id");
        if (id != null && id.startsWith(idPrefix))
        {
            return id;
        }
     }
     else
     {
        for (int i = 0; i < count; i++)
        {
           String id =
             getEmptySlotId(DOM.getChild(element, i), idPrefix);
           if (id != null)
           {  
              return id;
           }
        }
     }
     return null;
  }
}

as we are displaying JFreeChart's nice and cool chart, we are using JFreeChart's servlet service named - "displayChart". You can get more details on this from JFreeChart.


Now we will look at ChartService and ChartServiceAsync.

This is our ChartService Class:



package com.secl.chart.test.client.rpc;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.ServiceDefTarget;

public interface ChartService extends RemoteService
{
  public static final String SERVICE_URI = "chartService";

  String generateChart();

  public static class Util
  {
     public static ChartServiceAsync getInstance()
     {
        ChartServiceAsync instance =
        (ChartServiceAsync) GWT.create(ChartService.class);
        ServiceDefTarget target = (ServiceDefTarget) instance;
        target.setServiceEntryPoint(GWT.getModuleBaseURL()
                             + SERVICE_URI);
        return instance;
     }
  }
}




and our ChartServiceAsync class




package com.secl.chart.test.client.rpc;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface ChartServiceAsync
{
  void generateChart(AsyncCallback callback);
}



and ChartServiceImpl, implementation of ChartService interface that runs in server side. It generates the Chart Image indeed. You can read the datasource from your database if you need.


package com.secl.chart.test.server;

import java.awt.Color;
import java.awt.GradientPaint;
import javax.servlet.http.HttpSession;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.servlet.ServletUtilities;
import org.jfree.data.category.DefaultCategoryDataset;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.secl.chart.test.client.rpc.ChartService;

public class ChartServiceImpl extends RemoteServiceServlet implements ChartService {

public String generateChart()
{
String chartName = "";
JFreeChart chart = null;

chart = this.generateBarChart();
/*
* Save the chart as an '300px x 250px' jpeg image.
*/
try
{
HttpSession session = getThreadLocalRequest().getSession();
chartName = ServletUtilities.saveChartAsJPEG(chart, 300, 250, null, session);
}
catch (Exception e)
{
// handle exception
}

/*
* Finally, return the chart name to the caller.
*/
System.out.println("Name is: " + chartName);
return chartName;
}
private JFreeChart generateBarChart()
{
String series1 = "First";
String series2 = "Second";
String series3 = "Third";

// column keys...
String category1 = "Category 1";
String category2 = "Category 2";
String category3 = "Category 3";
String category4 = "Category 4";
String category5 = "Category 5";

// create the dataset...
DefaultCategoryDataset dataset = new DefaultCategoryDataset();

dataset.addValue(1.0, series1, category1);
dataset.addValue(4.0, series1, category2);
dataset.addValue(3.0, series1, category3);
dataset.addValue(5.0, series1, category4);
dataset.addValue(5.0, series1, category5);

dataset.addValue(5.0, series2, category1);
dataset.addValue(7.0, series2, category2);
dataset.addValue(6.0, series2, category3);
dataset.addValue(8.0, series2, category4);
dataset.addValue(4.0, series2, category5);

dataset.addValue(4.0, series3, category1);
dataset.addValue(3.0, series3, category2);
dataset.addValue(2.0, series3, category3);
dataset.addValue(3.0, series3, category4);
dataset.addValue(6.0, series3, category5);

JFreeChart chart = ChartFactory.createBarChart(
"Bar Chart Demo", // chart title
"Category", // domain axis label
"Views Qantity", // range axis label
dataset, // data
PlotOrientation.VERTICAL, // orientation
true, // include legend
true, // tooltips?
false // URLs?
);
return chart;
}
}





our web.xml may look some thing like this:
<?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>
FreeChart</display-name>
<servlet>
<servlet-name>ChartService</servlet-name>
<servlet-class>
com.secl.chart.test.server.ChartServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ChartService</servlet-name>
<url-pattern>/chartService</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>
DisplayChart
</servlet-name>
<servlet-class>
org.jfree.chart.servlet.DisplayChart
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DisplayChart</servlet-name>
<url-pattern>/displayChart</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
</web-app>

Ok that's all to run the gwt project. Simply you can run through Cypal's instruction.


Now we can start our work for JBossPortal,

Our ChartPortlet class is simple, we are creating the portlet here.
package com.secl.chart.test;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class ChartPortlet extends GenericPortlet
{
AtomicInteger counter = new AtomicInteger(0);

protected void doView(RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException, IOException
{
renderResponse.setContentType("text/html");
PrintWriter writer = renderResponse.getWriter();
writer.println("");
writer.close();
}

protected void doHelp(RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException, IOException
{
renderResponse.setContentType("text/html");
PrintWriter writer = renderResponse.getWriter();
writer.write("Help");
writer.close();
}
protected void doEdit(RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException, IOException
{
renderResponse.setContentType("text/html");
PrintWriter writer = renderResponse.getWriter();
writer.println("Edit");
writer.close();
}
}


Let us look at our descriptor files now,

chart-object.xml file

<?xml version="1.0" encoding="UTF-8"?>
<deployments>
<deployment>
<if-exists>overwrite</if-exists>
<parent-ref/>

<portal>
<portal-name>ChartPortal</portal-name>

<supported-modes>
<mode>view</mode>
<mode>edit</mode>
<mode>help</mode>
</supported-modes>
<supported-window-states>
<window-state>normal</window-state>
<window-state>minimized</window-state>
<window-state>maximized</window-state>
</supported-window-states>
<security-constraint>
<policy-permission>
<action-name>viewrecursive</action-name>
<action-name>personalizerecursive</action-name>
<unchecked/>
</policy-permission>
</security-constraint>
<page>
<page-name>default</page-name>
<properties>
<property>
<name>order</name>
<value>1</value>
</property>
</properties>
<window>
<window-name>ChartPortletWindow</window-name>
<instance-ref>ChartPortletInstance</instance-ref>
<region>left</region>
<height>0</height>
</window>
</page>
</portal>
</deployment>
</deployments>


portlet.xml file

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
version="1.0">

<portlet>
<portlet-name>ChartPortlet</portlet-name>
<portlet-class>com.secl.chart.test.ChartPortlet</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
<portlet-mode>HELP</portlet-mode>
</supports>
<portlet-info>
<title>Chart Portlet</title>
</portlet-info>
</portlet>
</portlet-app>


and finally, portlet-instances.xml

<?xml version="1.0" standalone="yes"?>
<deployments>
<deployment>
<instance>
<instance-id>ChartPortletInstance</instance-id>
<portlet-ref>ChartPortlet</portlet-ref>
</instance>
</deployment>
</deployments>


Infact this file joins our JBossPortal descriptor (chart-object.xml) and JSR 168 descriptor (portlet.xml).

Ok, thats IT!!! We are done. Need to run the build.xml now. But before that, make it sure, you set gwt.home and jbossportal.home properly inside your local.properties file.



Please download the sample and enjoy.

2 comments: