My name is Gurkan Erdogdu and I am the CTO of the MechSoft Mechanical and Software Solutions. I have been active in the Java and Java EE platform more than 10 years. Strong supporter for the Free and Open Source Software, and actively participating within the Open Source based foundations like, Apache Software Foundation, JBoss, and recently Open Web Foundation. I am the member of the Apache Software Foundation and Open Web Foundation. Writing blog at gurkanerdogdu@blogspot.com. Gurkan has posted 24 posts at DZone. View Full User Profile

Developing AJAX Web Applications with Defne Framework and Ext JS

07.28.2010
| 5435 views |
  • submit to reddit

Defne is a service oriented web application framework. The main motivation behind Defne is ease of use. Defne allows developers to concentrate on their business logic while it provides all other application requirements such as transaction and security. With Defne, you are able to implement powerful enterprise web application's business services easily.

Why do you use Defne?

  • Performancable, Defne services are stateless
  • Easy to implement business services
  • Easy to test business services (no need to use third party testing frameworks)
  • East to deploy (No need to use full blown Java EE application servers)
  • It is open source, Apache Software License Version 2
  • Currently used in production systems
  • Based on Java EE standards
  • And more...

Defne provides environment for writing Java EE business services easily. It does not provide any GUI related functionality. It can be used with JSF or any AJAX based JavaScript Libraries.

Required Runtime

Defne requires Java runtime greater than or equal to 1.5. Moreover, it requires OpenWebBeans and JPA runtimes for dependency injection and database operations, respectively.

SIwpas, Simple Web Profile Application Server, http://code.google.com/p/siwpas , provides necessary runtimes to use Defne framework. It is easy to install and use. If you have used Apache Tomcat, you are already an expert on SIwpas!

Defne Snapshots

You can get Defne snapshots from https://oss.sonatype.org/content/repositories/snapshots/org/defne/.

Article Content

In this article, we will show you how to implement a  very simple web application based on Defne Service Oriented Web Application Framework and powerful Ext JS JavaScript GUI Framework. GUI of the application is implemented with Ext JS while business services are implemented with Defne framework. Defne supports AJAX communication with its "json" module. Ext JS provides easy to use JavaScript methods to communicate with server side business logic over AJAX communication.

This article is based on the simple Survey Application. It contains a simple GUI that enables users to create survey records and listing them in Ext JS grid. Below screenshots give you a some feeling of how the application works!

Listing survey records,

 Adding survey records,

Those screen are implemented with powerful Ext JS JavaScript Framework.

Flow of the application 

  • User fills the survey appication form
  • Fills "Name of the survey", "Id of the survey", "Location of the survey","Date of the survey" and "Type of the survey"
  • Clicks Save button, Ext JS sends form payloads as JSON to server with AJAX
  • Defne gets JSON message and transforms it into the Service "Message"
  • Defne calls "addSurvey"operation of the SurveyService
  • Defne transforms Service "Message" into JSON payload
  • Ext JS shows succesfull dialog box to the user
Listing of the records has similar workflow, just services are changed.

Writing Defne Service

Below is the Defne service interface,

public interface ISurveyService
{
String SERVICE_NAME = "SurveyService";

interface AddOperation{
String OPERATION_NAME = "addSurvey";
interface INPUT {
String SURVEY = "SURVEY";
}

interface OUTPUT{
String MESSAGE = "MESSAGE";
}
}

interface ListOperation{
String OPERATION_NAME = "listSurveys";

interface OUTPUT{
String LIST = "LIST";
}
}

}

 And service implementation,

@Service
@SuppressWarnings("unchecked")
public class SurveyService
{

@Operation
@TransactionAttribute(TransactionPolicy.WITH_TRANSACTION)
@EntityManagerAttribute
public static Message addSurvey(Message message) throws DefneException
{
Survey survey = message.getMessageParameter(Survey.class, ISurveyService.AddOperation.INPUT.SURVEY);
EntityManager manager = EntityManagerUtil.getEntityManagerFromBag(message);

manager.persist(survey);

Message output = MessageFactory.newMessage();
output.putMessageParameter(ISurveyService.AddOperation.OUTPUT.MESSAGE, "Survey is added with id : " + survey.getId());

return output;
}

@Operation
@TransactionAttribute(TransactionPolicy.NO_TRANSACTION)
@EntityManagerAttribute
public static Message listSurveys(Message message) throws DefneException
{
EntityManager manager = EntityManagerUtil.getEntityManagerFromBag(message);
Query query = manager.createQuery("select s from Survey s");

List<Survey> surveys = (List<Survey>) query.getResultList();

Message oBag = MessageFactory.newMessage();
oBag.putMessageParameter(ISurveyService.ListOperation.OUTPUT.LIST, surveys);

return oBag;
}
}

 Defne heavily uses annotations for its vertical business requirements. Mostly used annotations and their meanings are,

  • @Service, makes Java class as Defne Service
  • @Operation, make Service class method as Defne service operation
  • @TransactionAttribute, defines service operation's  transaction requirements
  • @EntityManagerAttribute, defines whether or not service operation needs JPA. Some services may not use JPA.
  • @Roles, defines service operation's security requirements
Ext JS GUI Application

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>First EXT JS Page</title>
<link rel="stylesheet" href="js/resources/css/ext-all.css" style="text/css">
<script type="text/javascript" src="js/ext-base.js"></script>
<script type="text/javascript" src="js/ext.js"></script>
<script type="text/javascript" src="js/ext-lang-tr.js"></script>
<script type="text/javascript">

// Path to the blank image must point to a valid location on your server
Ext.BLANK_IMAGE_URL = 'js/resources/images/default/s.gif';

Ext.onReady(function(){

Ext.QuickTips.init();

// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';

var simpleAjax = function () {

var currentTime = Ext.getCmp('date').getValue();

var month = currentTime.getMonth() + 1
var day = currentTime.getDate()
var year = currentTime.getFullYear()


var dataToSend = {
'adaptorClass':'org.extjs.example.SurveryAdaptor',
'service':'SurveyService',
'method':'addSurvey',
'survey':{
'appId':Ext.getCmp('aid').getValue(),
'survId':Ext.getCmp('sid').getValue(),
'location':Ext.getCmp('location').getValue(),
'surveyDate': day + "/" + month + "/" + year,
'surveyType':Ext.getCmp('type').getValue()
}
};

var received = function (response) {
x = Ext.decode( response.responseText );
Ext.Msg.alert('Status', 'Survey with id : ' + x.MESSAGE + " registered succesfully");

var form = Ext.getCmp('surveyForm');
form.getForm().reset();

Ext.getCmp('aid').focus();

}

Ext.Ajax.request({
url: '/extjs-example/DefneJson',
success: received,
params: Ext.util.JSON.encode(dataToSend),
method:'post'
});

} // eo function

var bd = Ext.getBody();

var simple = new Ext.FormPanel({
id:'surveyForm',
labelWidth: 75,
frame:true,
title: 'Survey Form',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 230},
defaultType: 'textfield',

items: [{
fieldLabel: 'Name',
name: 'aid',
allowBlank:false,
id:"aid"
},{
fieldLabel: 'Id',
name: 'sid',
id:"sid"
},{
fieldLabel: 'Location',
name: 'location',
id:'location',
xtype:'combo',
store: ['Ankara','Istanbul','Mersin','NewYork','Tokyo'],
emptyText:'Select survey location...'

}, {
fieldLabel: 'Date',
name: 'date',
id:'date',
xtype : 'datefield',
format: 'd/m/Y'
},
{
fieldLabel: 'Type',
name: 'type',
id:'type',
xtype:'combo',
store: ['Simple','Averege','Complex'],
emptyText:'Select survey type...'
}
],

buttons: [
{
handler: loadSurveys,
text: 'Show Surveys...'
},
{
handler: simpleAjax,
text: 'Save'
},
{
text: 'Cancel',
handler: function(){
var form = Ext.getCmp('surveyForm');
form.getForm().reset();

Ext.getCmp('aid').focus();
}
}]
});

//Our survey model
var surveyModel= new Ext.data.Record.create(
[
{name:'appId'},
{name:'survId'},
{name:'location'},
{name:'surveyDate'},
{name:'surveyType'}
]);


//Our JSON reader
var surveyReader= new Ext.data.JsonReader(
{
root: 'surveys'
}, surveyModel);

//Our Store
var surveyStore = new Ext.data.Store({
reader: surveyReader
});

//Load our surveys from server
function loadSurveys(){

var surveyServiceData = {
'adaptorClass':'org.extjs.example.SurveryAdaptor',
'service':'SurveyService',
'method':'listSurveys'
};
Ext.Ajax.request({
url: '/extjs-example/DefneJson',
params : Ext.util.JSON.encode(surveyServiceData),
success: function(response){
var jsonData = Ext.util.JSON.decode(response.responseText);
surveyStore.loadData(jsonData);
},
method:'post'
});

};
var surveyGrid = new Ext.grid.GridPanel({

id: 'surveyGrid',
store: surveyStore,

columns: [
{
id : 'name',
header : 'Name',
width : 100,
sortable : true,
dataIndex : 'appId'
},
{
id : 'id',
header : 'Id',
width : 100,
sortable : true,
dataIndex : 'survId'
},
{
id : 'location',
header : 'Location',
width : 100,
sortable : true,
dataIndex : 'location'
},
{
id : 'date',
header : 'Date',
width : 100,
sortable : true,
dataIndex : 'surveyDate'
},
{
id : 'type',
header : 'Type',
width : 100,
sortable : true,
dataIndex : 'surveyType'
}
],

stripeRows: true,
height:400,
width:800

});//end of survey grid
var window = new Ext.Window({
id : 'simple-window',
width : 800,
height : 400,
layout : 'table',
layoutConfig : {columns : 1},
border : true,
closable : false,
title : 'Defne Java EE Web Framework and Ext JS Together',
items : [simple,surveyGrid]
});
window.show();
});
</script>
</head>
<body>
<div id="msg" style="visibility: hidden"></div>
</body>
</html>

For adding Survey records we create JSON payload and send it to server.

 	   var dataToSend = {
'adaptorClass':'org.extjs.example.SurveryAdaptor',
'service':'SurveyService',
'method':'addSurvey',
'survey':{
'appId':Ext.getCmp('aid').getValue(),
'survId':Ext.getCmp('sid').getValue(),
'location':Ext.getCmp('location').getValue(),
'surveyDate': day + "/" + month + "/" + year,
'surveyType':Ext.getCmp('type').getValue()
}
};

 Here is the required parts of this message

  • adaptorClass : It is the class that must be implemented by the Defne Service developer. It converts JSON payloads to Defne "Message" and vice versa.
  • service : Name of the Defne service
  • method : Name of the Defne service operation
  • survey  : This is used by the adaptor class to access JSON payloads.

Defne uses powerful JSON library, http://code.google.com/p/google-gson/ for converting JSON to Java objects. Here is the adaptor class,

public class SurveryAdaptor extends JsonServiceMessage
{
private static final long serialVersionUID = 1L;

private Survey survey;

@Override
@SuppressWarnings("unchecked")
public Object fromOutputMessage(Message message)
{
Object result = null;

if(getMethod().equals(ISurveyService.AddOperation.OPERATION_NAME))
{
Map<String, String> map = new HashMap<String, String>();
map.put("MESSAGE", message.getMessageParameter(String.class,ISurveyService.AddOperation.OUTPUT.MESSAGE));

result = map;
}
else if(getMethod().equals(ISurveyService.ListOperation.OPERATION_NAME))
{
Map<String, List<Survey>> surveys = new HashMap<String, List<Survey>>();
surveys.put("surveys", message.getMessageParameter(List.class, ISurveyService.ListOperation.OUTPUT.LIST));

result = surveys;
}

return result;
}

@Override
public Message toInputMessage()
{
Message message = MessageFactory.newMessage(getService(), getMethod());

if(getMethod().equals(ISurveyService.AddOperation.OPERATION_NAME))
{
message.putMessageParameter(ISurveyService.AddOperation.INPUT.SURVEY, this.survey);
}
else if(getMethod().equals(ISurveyService.ListOperation.OPERATION_NAME))
{
//Do nothing
}

return message;
}


}

 GSON librariy automatically maps JSON message to "JsonServiceMessage" objects. Below is the content of the Survey class

@Entity
public class Survey
{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;

@Column
private String appId;

@Column
private String survId;

@Column
private String location;

@Column
private String surveyDate;

@Column
private String surveyType;

public int getId()
{
return id;
}

public void setId(int id)
{
this.id = id;
}

public String getAppId()
{
return appId;
}

public void setAppId(String appId)
{
this.appId = appId;
}

public String getSurvId()
{
return survId;
}

public void setSurvId(String survId)
{
this.survId = survId;
}

public String getLocation()
{
return location;
}

public void setLocation(String location)
{
this.location = location;
}

public String getSurveyDate()
{
return surveyDate;
}

public void setSurveyDate(String surveyDate)
{
this.surveyDate = surveyDate;
}

public String getSurveyType()
{
return surveyType;
}

public void setSurveyType(String surveyType)
{
this.surveyType = surveyType;
}


}

 As you can see, Java class (Survey) field names are same with JSON payload field names.

That is it!!! You can deploy your application into SIwpas and hit the URL,

http://localhost:8080/extjs-example/addSurvey.html

Enjoy!!!!

You can look at the extjs-example from SVN , http://defne.googlecode.com/svn/trunk/defne-samples/extjs-example/

Web Site : http://code.google.com/p/defne/

SVN Site : http://code.google.com/p/defne/source/browse/

Maven Snapshot : https://oss.sonatype.org/content/repositories/snapshots/org/defne/

Defne Discussion : http://groups.google.com/group/defnedev

Published at DZone with permission of its author, Gurkan Erdogdu.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Roberto Yudice replied on Thu, 2010/07/29 - 11:47am

That's a lot of code for so simple application

Yaozong Zhu replied on Thu, 2010/08/19 - 3:44am

That's because both server and client sides are introduced. I think this thread potentially can be divided into two. Actually, Ext JS and Define are not coupled together.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.