340 likes | 488 Views
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
E N D
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/tutorials/ajax/DWRExample/
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() { return id; } private void setId(Long id) { this.id = id; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getTitle() { return title; } public void setTitle(String title) { 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/hibernate-mapping-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-mapping-3.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() { return sessionFactory; } } 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)
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> <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>
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> <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>
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
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/EventManager and copy and paste the script tags
More Dojo boilerplate • Paste the following too: <script type="text/javascript" src="js/dojo/dojo/dojo.js" 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>
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>
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 putEvents = function(list){ 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(); };
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>
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 <ui > <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>
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