You are on page 1of 84

Ajax for Java developers but without the suckage

Who is this jabronie? (aka: The Braggart Slide)


Frank W. Zammetti Developer/Lead/Architect/Whatever for PNC Global Investment Servicing Author of four books (fifth coming soon) and a couple of articles, tech reviewer on a number of other books Creator of Java Web Parts (APT most famously) and Struts-WS Current lead of DataVision One of the original developers of PocketHobbit Contributor to other OSS projects (Struts, Commons, etc.)

Open with a joke!

Cool, we can all go home now

Ajax (for those residing in rock abodes)


Asynchronous (almost always) JavaScript (almost always) XML (almost never) All about out-of-band requests and partial page loads

The beating of a dead Equine


Jesse James Garrett, Adaptive Path, February 2005 ITS NOTHING NEW!! Its about the concepts, not technology In some ways, it was the saviour of the Internet

That horse was askin for it!

Oh the humanity!

The big question: Why Ajax?


Richer, more responsive UIs (RIAs) Reduced network utilization (careful!) Revolution in the guise of evolution Its allows for a paradigm shift (once again, RIAs) Ajax isnt just a communication mechanism any more (RIA == Ajax these days)

An RIA (and a real looker of an ET!)

Another RIA

One more for good measure

About that suckage I spoke of


Ajax is hard to get right Many people dont like doing JavaScript Requires a certain expertise that not every shop has Puts the focus on HTTP

Build or buy?

The star of the show: DWR


Open-source, licensed under the ASL Member of the Dojo Foundation Java-only means no design compromises Implemented as a servlet, works fine in any container Minimizes JavaScript and deemphasizes servlet spec Makes calls to server-side code from JavaScript look the same as local calls Security is a core concept, not an afterthought Robust error handling Integration with many popular libraries and frameworks To put it simply: its an RPC mechanism for the Java webapps

RPC mystified

(someone told me lots of pictures in a slideshow is a good idea, even superfluous ones like this!)

DWRs brand of RPC

Yeah, but what does it actually DO?


Auto-generates a JavaScript proxy stub for a server-side Java object Handles marshalling of all inbound and outbound data Handles instantiation and calling appropriate methods of the server-side object Transparently handles all that icky Ajax stuff

DWR In a nutshell

The basics, part 1


Just add some JARs (dwr.jar and commonslogging.jar) and a servlet entry:
<servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping>

The basics, part 2


dwr.xml configures it:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd"> <dwr> <allow> <create creator="new javascript=MathDelegate> <param name="class" value="app.MathDelegate" /> </create> </allow> </dwr>

The basics, part 2a

The basics, part 3


A server-side class to call on:
package app; public class MathDelegate { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; } }

The basics, part 4

Some client-side code to call it:

<html> <head> <script type="text/javascript" src="dwr/interface/MathDelegate.js"></script> <script type="text/javascript" src="dwr/engine.js"></script> <script> function $(inID) { return document.getElementById(inID); } function doMath() { MathDelegate[$("op").value]($("num1").value, $("num2").value, function(answer) { $("divAnswer").innerHTML = answer } ); } </script> </head> <body> <input type="text" id="num1" size="4">&nbsp; <select id="op"> <option value="add">+</option><option value="subtract">-</option> <option value="multiply">*</option><option value="divide">/</option> </select> &nbsp;<input type="text" id="num2" size="4"> <input type="button" value="=" onClick="doMath();"> &nbsp;<span id="divAnswer" style="font-size:18pt;">&nbsp;</span><br><br> </body> </html>

math

Mmmm pudding AGHHAGGAGHAGAGA

(note to self: need to spell-check Homer Simpson biological drooling sound above)

Interfaces and the engine, and more


dwr/interface/* are where the dynamically generated JavaScript proxy stubs corresponding to remotable server classes are served from engine.js, the client-side engine behind DWR, is mostly static but with some dynamic elements Optionally, theres util.js Note that all of this is served by the DWR servlet

Call syntax
Two ways basic:
MathDelegate.add(2, 2, function(serverResponse) { alert(serverResponse); });

Call metadata object:


MathDelegate.add(2, 2, { callback : function(serverResponse) { alert(serverResponse); }, errorHandler : function() { alert(Were boned!); } });

Call syntax redux


In general, use the call metadata object paradigm Allows for passing of additional information (error handlers, options) Basic approach might be more readable if you really only need a callback

The lost art of debugging


Set debug servlet parameter to true http://server:port/context/dwr Lists all classes DWR can remote as well as test harnesses and even troubleshooting tips YOULL WANT TO TURN THIS OFF IN PRODUCTION

debugging

(anyone not impressed can leave their geek credentials at the door on the way out)

Yeah, neat, but what of performance?


Lots of reflection magic Dynamic code generation Youd think performance would be terrible, but youd be wrong! Interface files and util.js can be saved off and served from web server to take advantage of caching That DOES NOT work for engine.js!

Security-security-security-security Security-security-security-security!

Threats arent always as cute as this

Only what you want


Only classes listed in dwr.xml can be remoted Further, you can limit access at the method level:
<create creator="new"> <param name="class" value="app.MathDelegate" /> <include method="MyMethod" /> <!-- All others now excluded --> </create>

By default, all methods are available In production you probably should always use the above paradigm

Only who you want


Can limit access to J2EE roles at the method-level:
<create creator="new"> <param name="class" value="app.MathDelegate" /> <include method="MyMethod" /> <auth method="MyMethod" role="MyRole" /> </create>

Creators
Creators instantiate remotable objects Out of the box: new, none, spring, jsf, struts, pageflow, ejb3 Provides for integration with other libraries Can trivially create your own new and none are the most commonly used

Converters
Converters marshal beans from Java to JavaScript, and vice-versa Out of the box: boolean, byte, short, int, long, float, double, char, java.lang.Boolean, java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Character, java.lang.BigInteger, java.lang.BigDecimal, java.lang.String, arrays, collections and maps of most types, enum, java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp bean and object converters, most frequently used (object works with data members directly, bean uses accessors/mutators) You can of course create your own too

Beans, beans, theyre good for your heart, part 1


package app; public class SearchVO { private String acctNum; public void setAcctNum(String acctNum) { this.acctNum = acctNum; } public String getAcctNum() { return this.acctNum; } }

Beans, beans, theyre good for your heart, part 2


package app; public class Account { private String acctNum; private String shareholder; private Integer balance; public void setAcctNumber(String acctNum) { this.acctNum = acctNum; } public String getAcctNum() { return this.acctNum; } public void setShareholder(String sh) { this.shareholder = sh; } public String getShareholder() { return this.shareholder; } public void setBalance(Integer balance) { this.balance = balance; } public Integer getBalance() { return this.balance; } }

Beans, beans, theyre good for your heart, part 3


package app; public class Processor { public Account getAccount(SearchVO searchVO) { Account account = new Account(); account.setAcctNum(searchVO.getAcctNum()); account.setShareholder("Tapping, Amanda"); account.setBalance(new Integer(25986)); return account; } }

Beans, beans, theyre good for your heart, part 4


<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd"> <dwr> <allow> <convert converter="bean" match="app.SearchVO" /> <convert converter="bean" match="app.Account" /> <create creator="new javascript=Processor> <param name="class" value="app.Processor" /> </create> </allow> </dwr>

Beans, beans, theyre good for your heart, part 5

Here thar be JavaScript too:

<html><head> <script type="text/javascript" src="dwr/interface/Processor.js"></script> <script type="text/javascript" src="dwr/engine.js"></script> <script type="text/javascript" src="dwr/util.js"></script> <script> function doSearch() { var accountSearchVO = { acctNum : document.getElementById("acctNum").value }; Processor.getAccount(accountSearchVO, { callback : function(inAccount) { dwr.util.setValue("divAccountDetails", "Account Number: " + inAccount.acctNum + "<br>" + "Shareholder Name: " + inAccount.shareholder + "<br>" + "Current Balance: " + inAccount.balance, { escapeHtml : false } ); }}); } </script> </head><body> Account Number: <input type="text" id="acctNum"> <input type="button" value="Get Account" onClick="doSearch();"><br><br> <div id="divAccountDetails"></div> </body></html>

account

(* yes, that is indeed a cheap Watchmen reference!)

dwr.util
General-purpose client utilities, mostly concerned with getting content into the DOM, in no way DWR-specific addOptions() Add elements to lists (ol, ul, select) addRows() Add rows to tables byId() Shortcut to document.getElementById() getText() Get text (not value) of an <option> element getValue()/getValues() Get value of virtually any HTML element (it deals with the specifics of what value means for each) removeAllOptions() Remove all <option> from a <select> or ol/ul element removeAllRows() Remove all rows from a table setValue()/setValues() The reverse of getValue() toDescriptiveString() Output an object in a useful way

I hate it when a plan doesnt come together

Error handling in DWR


Can handle errors globally or on a per-call basis Warnings Things you can usually ignore dwr.engine.setWarningHandler(<function>) Errors - When DWR can tell you what went wrong (ex: server shuts down in the middle of servicing an AJAX request) dwr.engine.setErrorHandler(<function>); Exceptions - Thrown from server and propagated to client. Must handle exceptions per-call

Error Handling: The Next Generation


Processor.getAccount(accountSearchVO, { callback : function(inAccount) { dwr.util.setValue("divAccountDetails", "Account Number: " + inAccount.acctNumber + "<br>" + "Shareholder Name: " + inAccount.shareholder + "<br>" + "Current Balance: " + inAccount.balance, { escapeHtml : false } ); }, errorHandler : function(errMsg, exception) { alert(dwr.util.toDescriptiveString(exception, 4)); } });

Making sense of exceptions


Exceptions by default are not marshaled:
<convert match="java.lang.Exception" converter="exception"> <param name="include" value="message,lineNumber" /> </convert> <convert match="java.lang.StackTraceElement" converter="bean" />

error

(I feel so cheap for making that Lost in Space reference... UNCLEAN! UNCLEAN!)

Are you part of the XML is uncool crowd?


DWR also supports annotations:
@RemoteProxy public class WordsOfWisdom { @RemoteMethod public String getWisdom() { return <insert something wisdom-y here>; } }

Thats not quite all there is to it:


<servlet> <servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DWRServlet</servlet-class> <init-param><param-name>classes</param-name> <param-value>app.WordsOfWisdom</param-value> </init-param> </servlet>

Using HTTP objects


Sometimes a POJO isnt enough (session):
WebContext wc = WebContextFactory.get(); HttpSession session = wc.getSession();

But theres a better way:


Server: Public class Remote { public void myCallableMethod(String param, HttpSession session) { } } Client: Remote.myCallableMethod(test, { callback : function(resp) { alert(resp); } });
Reduced coupling to DWR Syntax is cleaner Less work (always good) Automagic stuff isnt usually good Must call data meta-object approach, so client-side code is arguably more verbose (slightly)

Call batching
Can combine multiple operations in one:
dwr.engine.beginBath(); BatchCallClass.method1(callback1); BatchCallClass.method2(callback2); SomeOtherClass.method2(callback3); dwr.engine.endBath(); function callback1() { alert(callback1); } function callback2() { alert(callback2); } function callback3() { alert(callback3); }

One network request made Order of calls and callback execution is guaranteed Lets you keep code separated on the server while maximizing runtime efficiency

Reading from other URLs


Read response from a URL and return as string from a method:
public class URLReader { public String read() throws ServletException, IOException { return WebContextFactory.get().forwardToString(/another.jsp); } }

Allows you to continue to use all the capabilities youre used to in JSP JSP becomes a (powerful) templating technology only Ties you to DWR Can only do forwards, so only the same context

Look out, here comes the Spring bandwagon!


Can delegate to Spring for bean instantiation:
dwr.xml: <create creator="spring" javascript="HelloHuman"> <param name="beanName" value="HelloHuman" /> <param name="location" value="spring-beans.xml" /> </create> spring-beans.xml: <?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="HelloHuman" class="app.HelloHuman" /> </beans>

alltogether

(awww, taking it APART is so much more fun!)

When forward isnt cool enough: Reverse Ajax

If pro is the opposite of con, then isnt progress the opposite of congress?!?
Server-push Difficult to implement on your own (thank you DWR!) Passive and active modes gives you lots of flexibility Active mode (comet specfically) chews up threads on server and proxies Good in small-scale apps, use with extreme caution beyond that (depending on method) Special servers/extensions exist to alleviate scalability concerns (depending on method) Speaking of methods

The three horsemen of the apocalypse, part 1: Piggybacking

Obviously not real push, but a decent approximation and often times good enough. Least resource-intensive (passive method).

The three horsemen of the apocalypse, part 2: Polling

Also not real push, but closer than piggybacking. Medium resource utilization (active method, but controllable).

Introducing New and Improved Comet!

(WARNING!! TRADEMARK INFRINGEMENT ALERT!!)

The three horsemen of the apocalypse, part 3a: Lets try this Comet thing again

As close to real push as youre going to get with HTTP. Its nothing but a hack but an extremely clever one! (true active method)

The mechanics of reverse Ajax: Piggybacking


To activate piggybacking:

DO NOTHING!
You can automatically piggyback on any incoming request

The mechanics of reverse Ajax: Polling


To activate polling, add to client code:
dwr.engine.setActiveReverseAjax(true);

Then, add to DWR servlet config in web.xml:


<init-param> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>org.directwebremoting.extend.ServletLoadMonitor</param-name> <param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value> </init-param> <init-param> <param-name>timeToNextPoll</param-name> <param-value>1000</param-value> </init-param>

The mechanics of reverse Ajax: Comet


To activate comet, add to client code:
dwr.engine.setActiveReverseAjax(true);

Then, add to DWR servlet config in web.xml:


<init-param> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param>

What about the J in JUG?!?


From the server, do something like:
Util.setValue("divRAResponse", "Hello!", true);

All users currently viewing the page Ajax calls are sourced from will see Hello! in <divRAResponse>.

When you dont want to talk to just anyone


To do something for a single user:
ScriptBuffer script = new ScriptBuffer(); script.appendScript(doSomething();"); WebContext wc = WebContextFactory.get(); ScriptSession scriptSession = wc.getScriptSession(); scriptSession.addScript(script);

The doSomething() Javascript function will be executed in the browser of the user belonging to the session associated with the request being serviced.

Running in the background


You can spawn a background thread, and then do reverse Ajax from it Just need to cache the WebContext object Applies to communication with a single user as well as all users Careful! Spawning threads in a servlet contanier is bad, mkay?

ra_*

(except in the case of UFOs apparently)

Binary files
Can handle binary files (up and down) Can deal with byte[], java.awt.BufferedImage, java.io.inputStream or org.directwebtemoting.io.FileTransfer (gives access to filename, mime type and contents) Uploading: Easier than commons-fileupload or similar and can integrate with progress bar widgets Downloading: easier than creating a special PDF servlet or similar What, you dont believe me? Ok, here you go:

Server: public class Remote { public void recieveFile(byte[] f) { /* Do something with contents. */ } public FileTransfer getFile() { // buf if a ByteArayOutputStream with the contents of a PDF in it. return new FileTransfer(myFile.pdf, application/pdf, buf.toByteArray()); } } Client: <input type=file id=myFile> // Send file. var f = dwr.util.getValue(myFile); Remote.recieveFile(f); // Receive file. Remote.getFile(null, function(pdf) { dwr.engine.openInDownload(pdf); });

Poor mans web services


DWR supports JSON, JSON-P (so-called REST-based web services) Allows cross-domain access to DWR remotable classes Allows non-DWR clients to interact with DWR-exposed remotables Not perfect (manual Ajax, parameter naming, etc), but still very nice! Server: public class Demo { public String sayHi(String name) { return Hi there, + name; } } Command Line: $ wget http://localhost/app/dwr/jsonp/Demo/sayHi?param0=Frank&callback=jsfunc -> jsfunc(Hi there, Frank);

Varargs
Avoids wrapping arguments in an array, collection or VO Can break edge cases when mixing servlet parameters and regular parameters
Server: public class VarArgClass { public void meth(String arg) { /* Do something */ } } Client: VarArgClass.meth(Apollo, Starbuck, Boomer);

Overloaded methods
Prior to v3, overloaded methods were indeterminate (might get the right one, might not) Finally, in v3, true overloading support is present!
Server: public class Remoted { public void method(int num) { System.out.println(num: + num); } public void method(String str) { System.out.println(str: + str); } } Client: Remoted.method(I am a string); Remoted.method(42);

Tie-ins with Dojo and others


Dojo data stores Server-side manipulation of Dijits Deep Tibco General Interface (nearly full control of the entire UI from the server)

Reverse Ajax upgrades


More scalable (maybe), more robust API
// Broadcast to all users currently viewing index.html page. Browser.withCurrentPage(index.html, new Runnable() { public void run() { Window.alert(Hello); } }); // Broadcast to everyone connected to DWR, regardless of current page. Browser.withAllSessions(); // Broadcast to a filtered subset of users. Browser.withFiltered(scriptSessionFilter, ); // Broadcast to a specific user. Browser.withSession(sesssionID, );

Reverse Ajax upgrades continued


The server-side API is more robust, allowing for easier manipulation of the client from server code:
Element e = doc.createElement(p); ScriptSessions.addFunctionCall(document.body.appendChild, e); ScriptSessions.addScript(alert(Hello Zark!);); Document.setCookie(new Cookie(name, value)); String[] opts = new String[] { Kruger, Myers, Vorhees }; Util.addOptions(li, opts); Effect.fade(someID);

In conclusion
DWR kicks more arse than anything that has ever kicked arse before (and thats only a slight exaggeration!)- simple, robust, powerful, secure* In short: if youre a Java developer who does Ajax, and you do it with something other than DWR, you almost certainly want marijuana legalized! To sum it all up succinctly

* and the opposite sex will find you more attractive for using it!

If you dont use DWR, you might be as dumb as her

(at least she has looks and BTW, mad props to Mario for not cracking up big-time!)

(And if youd like to contact me after the show Id be err, surprised actually but if you do: fzammetti@omnytex.com)

You might also like