84
Ajax for Java developers… but without the suckage

DWR Presentation

Embed Size (px)

Citation preview

Page 1: DWR Presentation

Ajax for Java developers… but without the suckage

Page 2: DWR Presentation

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.)

Page 3: DWR Presentation

Open with a joke!

Page 4: DWR Presentation

Cool, we can all go home now…

Page 5: DWR Presentation

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

Page 6: DWR Presentation

The beating of a dead Equine

• “Jesse” James Garrett, Adaptive Path, February 2005

• IT’S NOTHING NEW!!

• It’s about the concepts, not technology

• In some ways, it was the saviour of the Internet

Page 7: DWR Presentation

That horse was askin’ for it!

Page 8: DWR Presentation

Oh the humanity!

Page 9: DWR Presentation

The big question: Why Ajax?

• Richer, more responsive UIs (RIAs)

• Reduced network utilization (careful!)

• Revolution in the guise of evolution

• It’s allows for a paradigm shift (once again, RIAs)

• Ajax isn’t just a communication mechanism any more (RIA == Ajax these days)

Page 10: DWR Presentation

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

Page 11: DWR Presentation

Another RIA

Page 12: DWR Presentation

One more for good measure

Page 13: DWR Presentation

About that “suckage” I spoke of

• Ajax is hard to get right

• Many people don’t like doing JavaScript

• Requires a certain expertise that not every shop has

• Puts the focus on HTTP

Page 14: DWR Presentation

Build or buy?

Page 15: DWR Presentation

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

Page 16: DWR Presentation

RPC mystified

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

Page 17: DWR Presentation

DWR’s brand of RPC

Page 18: DWR Presentation

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

Page 19: DWR Presentation

DWR In a nutshell

Page 20: DWR Presentation

The basics, part 1

• Just add some JARs (dwr.jar and commons-logging.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>

Page 21: DWR Presentation

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>

Page 22: DWR Presentation

The basics, part 2a

Page 23: DWR Presentation

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; }

}

Page 24: DWR Presentation

The basics, part 4

Some client-side code to call it:

Page 25: DWR Presentation

<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>

Page 26: DWR Presentation

Mmmm… pudding… AGHHAGGAGHAGAGA

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

math

Page 27: DWR Presentation

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, there’s util.js

• Note that all of this is served by the DWR servlet

Page 28: DWR Presentation

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(“We’re boned!”); }});

Page 29: DWR Presentation

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

Page 30: DWR Presentation

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

• YOU’LL WANT TO TURN THIS OFF IN PRODUCTION

Page 31: DWR Presentation

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

debugging

Page 32: DWR Presentation

Yeah, neat, but what of performance?

• Lots of reflection magic

• Dynamic code generation

• You’d think performance would be terrible, but you’d 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!

Page 33: DWR Presentation

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

Page 34: DWR Presentation

Threats aren’t always as cute as this

Page 35: DWR Presentation

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

Page 36: DWR Presentation

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>

Page 37: DWR Presentation

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

Page 38: DWR Presentation

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

Page 39: DWR Presentation

Beans, beans, they’re 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; }

}

Page 40: DWR Presentation

Beans, beans, they’re 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; }

}

Page 41: DWR Presentation

Beans, beans, they’re 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;

}

}

Page 42: DWR Presentation

Beans, beans, they’re 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>

Page 43: DWR Presentation

Beans, beans, they’re good for your heart, part 5

Here thar be JavaScript too:

Page 44: DWR Presentation

<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>

Page 45: DWR Presentation

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

account

Page 46: DWR Presentation
Page 47: DWR Presentation
Page 48: DWR Presentation

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

Page 49: DWR Presentation

I hate it when a plan doesn’t come together

Page 50: DWR Presentation

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…

Page 51: DWR Presentation

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)); }});

Page 52: DWR Presentation

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" />

Page 53: DWR Presentation

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

error

Page 54: DWR Presentation

Are you part of the “XML is uncool” crowd?

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

• That’s 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>

Page 55: DWR Presentation

Using HTTP objects

• Sometimes a POJO isn’t enough (session):WebContext wc = WebContextFactory.get();HttpSession session = wc.getSession();

• But there’s 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 isn’t usually good… Must call data meta-object

approach, so client-side code is arguably more verbose (slightly)

Page 56: DWR Presentation

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

Page 57: DWR Presentation

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 you’re used to in JSP

• JSP becomes a (powerful) templating technology only

• Ties you to DWR• Can only do forwards, so only the same context

Page 58: DWR Presentation

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>

Page 59: DWR Presentation

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

alltogether

Page 60: DWR Presentation

When forward isn’t cool enough: Reverse Ajax

Page 61: DWR Presentation

If pro is the opposite of con, then isn’t 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…

Page 62: DWR Presentation

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).

Page 63: DWR Presentation

The three horsemen of the apocalypse, part 2: Polling

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

Page 64: DWR Presentation

Introducing New and Improved Comet!

(WARNING!! TRADEMARK INFRINGEMENT ALERT!!)

Page 65: DWR Presentation

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

As close to real “push” as you’re going to get with HTTP. It’s nothing but a hack… but an extremely clever one! (true active method)

Page 66: DWR Presentation

The mechanics of reverse Ajax: Piggybacking

• To activate piggybacking:

DO NOTHING!

• You can automatically piggyback on any incoming request

Page 67: DWR Presentation

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>

Page 68: DWR Presentation

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>

Page 69: DWR Presentation

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>.

Page 70: DWR Presentation

When you don’t 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.

Page 71: DWR Presentation

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, m’kay?

Page 72: DWR Presentation

(except in the case of UFOs apparently)

ra_*

Page 73: DWR Presentation
Page 74: DWR Presentation

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 don’t believe me? Ok, here you go:

Page 75: DWR Presentation

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);});

Page 76: DWR Presentation

Poor man’s 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”);

Page 77: DWR Presentation

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”);

Page 78: DWR Presentation

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);

Page 79: DWR Presentation

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)

Page 80: DWR Presentation

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, …);

Page 81: DWR Presentation

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”);

Page 82: DWR Presentation

In conclusion

• DWR kicks more arse than anything that has ever kicked arse before (and that’s only a slight exaggeration!)- simple, robust, powerful, secure*

• In short: if you’re 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!

Page 83: DWR Presentation

If you don’t 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!)

Page 84: DWR Presentation

(And if you’d like to contact me after the show I’d be… err, surprised actually… but if you do: [email protected])