Saturday, July 25, 2009

Struts2 + Struts2 JSF plugin + ICE Faces

I found that there are a lot of people are looking for example or guide to make ICEfaces work together in a Struts 2 framework. Perhaps somebody who know the way can give some guides here...

Tuesday, July 7, 2009

GWT plugin for Struts

The source of this tutorial: http://cwiki.apache.org/S2PLUGINS/gwt-plugin.html

This plugin can be used to call methods on Struts actions using Google Web Toolkit (GWT):

Name GWT Plugin
Publisher Musachy Barroso, Sean McRae
License Open Source (ASL2)
Version 0.2 stable
Compatibility Struts 2.0.2+
Homepage http://code.google.com/p/struts2gwtplugin/
Download http://code.google.com/p/struts2gwtplugin/downloads/list

Example

Setup Action

Start by creating an action that contains the method that is going to be called. This method needs to match the signature of the service interface, but you don't need to implement the interface on the action. This method will be different from regular Struts 2 action methods in that it returns any object (that GWT can serialize), instead of a result name. If you want to call regular actions from GWT (not using GWT's RPC) go here

In this example the action "Hello" will return the string passed to it. Remember, the returned object is not the regular Struts result, it is the object that you will receive on the client side.

Example:

public class Hello {
public String hello(String text) {
return text;
}
}

Write the mapping for the action

For any action mapping that is going to be called from GWT:

  1. Add the map inside a package that extends "gwt-default"
  2. Add the "gwt" interceptor to the mapping

Example:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />

<package name="example" extends="gwt-default">
<action name="Hello" class="example.Hello" method="hello">
<interceptor-ref name="gwt"/>
</action>
</package>

</struts>

GWT classes

Write the required service interface as described here. This is regular GWT stuff.

As an example of the interface:

public interface MyService extends RemoteService {
public String hello(String s);
}

public interface MyServiceAsync {
public void hello(String s, AsyncCallback callback);
}

And to call it:

MyServiceAsync service = (MyServiceAsync) GWT.create(MyService.class);
AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
Window.alert(result.toString());
}

public void onFailure(Throwable caught) {
Window.alert(caught.toString());
}
};
ServiceDefTarget endpoint = (ServiceDefTarget) service;
endpoint.setServiceEntryPoint("Hello.action");
service.hello("Hi There!", callback);

Installation

This plugin can be installed by copying the plugin jar and gwt-servlet.jar (from GWT) into your application's /WEB-INF/lib directory. No other files need to be copied or created.

Integrate GWT into Struts 2 Application

Source of this tutorial: http://cwiki.apache.org/WW/struts-2-gwt.html

These tutorial will demonstrate how to call an Struts 2 action, using GWT to submit a form and use the returned data.

Write your action.

Example action:

"Hello.java"
package example;

import com.opensymphony.xwork2.Action;

public class Hello {
private String firstName;
private String lastName;

public String execute() {
return Action.SUCCESS;
}

public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}

Write the mapping for your action.

In this example the mapping is in struts.xml:

struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<package name="example" extends="struts-default">
<action name="Hello" class="example.Hello">
<result type="json" />
</action>

<action name="Main" class="com.opensymphony.xwork2.ActionSupport">
<result>org.apache.struts.gwt.Main/Main.jsp</result>
</action>
</package>

</struts>




The "Hello" action has a result of type "json", which will serialize the action object into a JSON

string. If "firstName" is set to "John" and "lastName" is set to "Galt", the output of the "Hello" action will be:

{
"firstName" : "John",
"lastName" : "Galt"
}

The "Main" action points to the "Main.jsp" page which is the page that uses GWT. The path of the result is "org.apache.struts.gwt.Main/Main.jsp". That means that the files generated by GWT must go under a folder named "org.apache.struts.gwt.Main" under the root of your application. See "Deployment structure" for more details.

Write "Main.jsp" page.

This is the page that is generated by GWT's "applicationCreator". It has been renamed to .jsp because we have modified it to be a jsp page, instead of a plain html page.

Main.jsp

<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<s:head theme="ajax" debug="true"/>
<meta name='gwt:module' content='org.apache.struts.gwt.Main/org.apache.struts.gwt.Main'>
</head>

<body>
<script language="javascript" src="${pageContext.request.contextPath}/org.apache.struts.gwt.Main/gwt.js"/>

<form id="form1">
<input type="text" name="firstName">
<br/>
<input type="text" name="lastName">
<span id="slot1"></span>
</form>

<br/>
<span id="slot2"></span>
</body>
</html>

We set head's tag attribute "theme" to ajax to use Dojo, don't panic, you won't have to use it directly. Note that we have changed a few things from the original html page generated by GWT, we set "content" to "org.apache.struts.gwt.Main/org.apache.struts.gwt.Main" because the GWT generated files will be under "AppRoot/org.apache.struts.gwt.Main" instead of beneath root, and we set "src" to "${pageContext.request.contextPath}/org.apache.struts.gwt.Main/gwt.js" for the same reason. Without these two changes the GWT files wouldn't be loaded.
Struts2GWTHelper

This class will take care of making the request. Why? Why do I need this class? Couple of reasons, first, there is a bug on bug on HTTPRequest.asyncPost which doesn't encode the payload properly, second, if you want to submit a form, you have to encode it yourself, and this class will help you do that. Optionally you can download a jar containing this class (with more methods) from here, add this to your GWT application file (i.e Main.gwt.xml):

<inherits name='struts2gwt.Struts2GWT'/>

and add the jar to the classpath in your compile script (i.e Main-compile.cmd) and the compile script (i.e Main-shell.cmd).
Struts2GWTHelper .java

package org.apache.struts.gwt.client;

import com.google.gwt.user.client.ResponseTextHandler;

public class Struts2GWTHelper {

/**
* Make asynchronous post
* @param url Action url
* @param formId id of form that will be posted
* @param handler callback function
*/
public static native void asyncPost(String url, String formId, ResponseTextHandler handler) /*-{
dojo = $wnd.dojo;
//don't use the dojo.io.bind({...}) shortcut, it doesn't work here
var request = new dojo.io.Request(url);
request.load = function(type, data, request) {
handler.@com.google.gwt.user.client.ResponseTextHandler::onCompletion(Ljava/lang/String;)(data);
};
request.formNode = dojo.byId(formId);
request.method = "POST";
$wnd.dojo.io.bind(request);
}-*/;

/**
* Make asynchronous post
* @param url Action url
* @param handler callback function
*/
public static void asyncPost(String url, ResponseTextHandler handler) {
Struts2GWTHelper.asyncPost(url, handler);
}
}

See? It wasn't that bad.
Write your GWT entry point
Main.java

package org.apache.struts.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.ResponseTextHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class Main implements EntryPoint {
final Label label = new Label();

/**
* This is the entry point method.
*/
public void onModuleLoad() {
final Button button = new Button("Click me");

button.addClickListener(new ClickListener() {
public void onClick(Widget sender) {
makeRequest();
}
});

RootPanel.get("slot1").add(button);
RootPanel.get("slot2").add(label);
}

private void makeRequest() {
Struts2GWTHelper.asyncPost("Hello.action", "form1", new ResponseTextHandler() {
public void onCompletion(String responseText) {
JSONObject obj = (JSONObject)JSONParser.parse(responseText);
JSONString firstName = (JSONString)obj.get("firstName");
JSONString lastName = (JSONString)obj.get("lastName");
label.setText("Welcome " + firstName.stringValue() + " " + lastName.stringValue());
}
});
}
}

The makeRequest() method will make a request to the "Hello" action that we created before, and will pass the fields of the form "form1" as parameters. Which will be populated on the action, and serialized back as JSON. Using JSONParser the JSON string is parsed into a JSON object. It is definitely not type safe as GWT Remoting, but it works.
Create Main.gwt.xml

Nothing new on this file, just as reference:
Main.gwt.xml

<module>

<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.json.JSON'/>

<!-- Specify the app entry point class. -->
<entry-point class='org.apache.struts.gwt.client.Main'/>

</module>

Deployment structure
Folder Notes
AppRoot The application deployment folder
AppRoot/index.html Welcome Page
AppRoot/org.apache.struts.gwt.Main Content generated by GWT (usually it gets generated into a folder named 'www')
AppRoot/WEB-INF Regular webapp files and classes


A GWT app: HouseOnMaps.com

Here got a web site 100% developed in GWT. It's House On Maps Dot Com @ www.houseonmaps.com

I think the Google map is loaded a bit slow. Perhaps there should be a trick to make it faster.

GWT-Ext


Source: http://gwt-ext.com/ (official site of GWT-Ext)
GWT-Ext is a powerful widget library that provides rich widgets like Grid with sort, paging and filtering, Tree’s with Drag & Drop support, highly customizable ComboBoxes, Tab Panels, Menus & Toolbars, Dialogs, Forms and a lot more right out of the box with a powerful and easy to use API. It uses GWT and Ext.

GWT-Ext 2.0.5 has been released. Grab the distribution from the Downloads area. GWT-Ext 2.0.5 supports GWT 1.5 final (1.5.2), Firefox3 and Chrome.

Note that GWT-Ext only supports Ext 2.0.2 that has a LGPL license option.

The download link for current latest version of GWT-Ext (GWT-Ext 2.0): http://gwt-ext.com/demo