Direct Web Remoting, Hibernate and Dojo.

E
“Easy Ajax for Java” Joel Barciauskas October 3, 2008

What is DWR?
• “Easy Ajax for Java” implies two parts
– Server – Java servlet – Client – JavaScript auto-generated by the Java servlet

• Enables JSON to JavaBean serialization • Exposes selected methods and JavaBean properties on the server as client-side JavaScript methods and objects • Reverse Ajax – IMB-like functionality

Why use DWR? Why not?
• Pros
– Avoid replication of effort defining model objects on both server and client – No XHR boilerplate required – No serialization boilerplate – JSON is fastest format to serialize/deserialize in the browser

• Cons
– Less control over network requests  E.g., harder to bundle requests – Not RESTful, all requests processed through POST data rather than URLs  Best for single-page applications

Let’s see it
• Application: Simple database create and read • Using: JavaScript, DWR 2.0, Hibernate 3.0, Derby

• Shell: http://source.nexaweb.com/svn/repos/trunk/tu

Create your domain class (JavaBean) • src/events/Event.java
package events; import java.util.Date; public class Event { private Long id; private String title; private Date date; public Event() {} public Long getId() { public Date getDate() { public String getTitle() { } return id; } } } } } } private void setId(Long id) { public void setDate(Date date) { public void setTitle(String title) { this.id = id; return date; return title;

this.date = date; this.title = title;

Create Hibernate XML Mapping
• src/events/Event.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernatemapping-3.0.dtd"> <hibernate-mapping> <class name="events.Event" table="EVENTS"> </class> </hibernate-mapping>

Add Property Mappings
• src/events/Event.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping3.0.dtd"> <hibernate-mapping> <class name="events.Event" table="EVENTS"> <id name="id" column="EVENT_ID"> <generator class="native"/> </id> <property name="date" type="timestamp" column="EVENT_DATE"/> <property name="title"/> </class> </hibernate-mapping>

Create Hibernate Configuration
• src/hibernate.cfg.xml <?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration>
<session-factory> <!-- Database connection settings --> <property name="connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property> <property name="connection.url“>jdbc:derby:eventDB;create=true</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.DerbyDialect</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">create</property> <mapping resource="events/Event.hbm.xml"/> </session-factory>

</hibernate-configuration>

Create SessionFactory instance
• src/util/HibernateUtil.java package util; import org.hibernate.*; import org.hibernate.cfg.*; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { } Note: This can also be achieved through the Spring integration – “use the Spring OpenSessionInViewFilter which will ensure that a Hibernate Session is open” (http:// directwebremoting.org/dwr/server/hibernate) return sessionFactory; }

Create an Event Manager
• src/events/EventManager.java package events; import org.hibernate.Session; import java.util.Date; import java.util.List; import util.HibernateUtil; public class EventManager { public void createAndStoreEvent(String title, Date theDate) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); session.save(theEvent); session.getTransaction().commit(); } public List listEvents() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List result = session.createQuery("from Event").list(); session.getTransaction().commit(); return result; } }

Add the DWR servlet to web.xml
• WebContent/WEB-INF/web.xml <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <servlet-class> org.directwebremoting.servlet.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> <servlet>

Create a Remote Proxy and Expose Remote Methods
• Src/events/EventManager.java

package events; import org.directwebremoting.annotations.RemoteMethod; import org.directwebremoting.annotations.RemoteProxy; import org.hibernate.Session; import java.util.Date; import java.util.List; import util.HibernateUtil; @RemoteProxy public class EventManager { @RemoteMethod public void createAndStoreEvent(String title, String theDate) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDateString(theDate); session.save(theEvent); session.getTransaction().commit(); } @RemoteMethod public List listEvents() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List result = session.createQuery("from Event").list(); session.getTransaction().commit(); return result; } }

Annotate Event as a Data Transfer Object
• src/events/Event.java package events; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import org.directwebremoting.annotations.DataTransferObject; import org.directwebremoting.annotations.RemoteProperty; @DataTransferObject public class Event { @RemoteProperty private Long id; @RemoteProperty private String title; private Date date; public Event() {} public Long getId() { return id; } private void setId(Long id) { this.id = id; } @RemoteProperty public String getDateString() { SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); return sdf.format(this.date); } [continued on next slide]

Annotate Event as a Data Transfer Object (con’t)
• src/events/Event.java [continued] @RemoteProperty public void setDateString(String dateStr) { SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); try { this.date = sdf.parse(dateStr); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } }

Note: We added getDateString and setDateString here, because there is no implicit conversion available between JavaScript dates and Java dates – mostly arrays, strings, BigNumber, and primitives. See http://directwebremoting.org/dwr/server/dwrxml/converters for more information.

Update web.xml
• WebContent/WEB-INF/web.xml <servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <init-param> <param-name>classes</param-name> <param-value> events.Event, events.EventManager </param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet>

Using DWR Debug Mode
• Go to http://localhost:8080/DWRExample/dwr/
– – – Click “EventManager” Enter “Event 1” in the first parameter of createAndStoreEvent Enter “10/01/2008” in the second parameter

• Click the Execute button

• Click the Execute button next to listEvents()

Let’s add DWR on the client side
• File->New->Web->HTML-> WebContent/index.html • Open and edit title “DWR Example”

• Go back to http://localhost:8080/DWRExample/dwr/test/E and copy and paste the script tags

More Dojo boilerplate
• Paste the following too: djConfig="isDebug: true, parseOnLoad: true"></script> <link rel="stylesheet" type="text/css" href="js/dojo/dojo/resources/dojo.css" /> <link rel="stylesheet" type="text/css" href="js/dojo/dijit/themes/tundra/tundra.css" /> <link rel="stylesheet" type="text/css" href="js/dojo/dojox/grid/_grid/Grid.css" /> <link rel="stylesheet" type="text/css" href="js/dojo/dojox/grid/_grid/tundraGrid.css" /> <script type="text/javascript"> dojo.require("dijit.form.Button"); dojo.require("dijit.form.TextBox"); dojo.require("dijit.form.DateTextBox"); dojo.require("dojox.grid.Grid"); dojo.require("dojo.data.ItemFileReadStore"); dojo.require("dojoe.dojoe"); </script> <script type="text/javascript" src="js/dojo/dojo/dojo.js"

Add Dojo.E
• Add tundra class to body tag: <body class="tundra"> • Create XML file WebContent/dwrDojoE.xml • And add tag <script type="text/xml" dojoType="dojoe.XmlScript" src="dwrDojoE.xml"></script>

Edit Dojo.E
• WebContent/dwrDojoE.xml <ui xmlns="html" xmlns:dijit="dijit" xmlns:dojox="dojox"> <dijit:form.Button> Refresh </dijit:form.Button> <dojox:grid.Grid id="gridNode" jsId="grid" elasticView="0" style="height:300px; width:500px"/> </ui>

Where’s the Grid?!?

Create a grid layout
• WebContent/index.html <body class="tundra">' <script type="text/javascript"> var layout = [{ cells: [[ { name: 'Event ID', field: 'id', width: 'auto' }, { name: 'Date', field: 'dateString', width: 'auto' }, { name: 'Event', field: 'title', width: 'auto' }]] }]; </script>

Note the correlation between the field values above and the RemoteProperty defintions of Event.java

Add the layout to the Grid
• WebContent/dwrDojoE.xml <ui > <dijit:form.Button> Refresh </dijit:form.Button> <dojox:grid.Grid id="gridNode" structure=“layout” jsId="grid" elasticView="0" style="height:300px; width:500px"/> </ui>

Now let’s get some data
• WebContent/index.html var dataStore = new Array; dataStore["items"] = list; dataStore["label"] = "title"; var model = new dojox.grid.data.DojoData(null, null, { jsId: 'model', store: new dojo.data.ItemFileReadStore({ data: dataStore }), query: { title: '*' } }); grid.setModel(model); grid.refresh(); grid.render(); }; var putEvents = function(list){

Update Dojo.E
• WebContent/dwrDojoE.xml <ui> <dijit:form.Button onClick="EventManager.listEvents(putEvents)"> Refresh </dijit:form.Button> <dojox:grid.Grid id="gridNode" jsId="grid" elasticView="0" style="height:300px; width:500px"/> </ui>

Click Refresh

Create a method to create an event
• WebContent/index.html var createEvent = function(titleId, dateId){ var title = dojo.byId(titleId).value; var date = dojo.byId(dateId).value; EventManager.createAndStoreEvent(title, date); EventManager.listEvents(putEvents); };

Let’s Add Input Controls
• WebContent/dwrDojoE.xml <dijit:form.Button onClick="EventManager.listEvents(putEvents)"> Refresh </dijit:form.Button> <dojox:grid.Grid id="gridNode" jsId="grid" structure="layout" elasticView="0“ style="height:300px; width:500px"/> <label for="eventTitle" style="float: left;">Event Title:</label> <dijit:form.TextBox style="float: left;" id="eventTitle" value=""/> <label for="eventDate" style="float: left;">Event Date:</label> <dijit:form.DateTextBox id="eventDate" value="" style="float: left;"/> <dijit:form.Button style="float: left;“ onclick="createEvent('eventTitle', 'eventDate')" label="Add Event"> </dijit:form.Button> </ui> <ui >

Reload the page

Enter a title and date

Load data at startup
• WebContent/index.html dojo.addOnLoad(function() { EventManager.listEvents(putEvents); });

Final note
• If using more complex object models with Hibernate, use the HibernateBeanConverter3 – See http:// directwebremoting.org/dwr/server/hibernate