Ajax for Java developers but without the suckage Who is this - - PowerPoint PPT Presentation

ajax for java developers but without the suckage
SMART_READER_LITE
LIVE PREVIEW

Ajax for Java developers but without the suckage Who is this - - PowerPoint PPT Presentation

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)


slide-1
SLIDE 1

Ajax for Java developers… but without the suckage

slide-2
SLIDE 2

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.)
slide-3
SLIDE 3

Open with a joke!

  • We've heard that a million monkeys at a million keyboards could produce the

complete works of Shakespeare; now, thanks to the Internet, we know that is not true.

– Robert Wilensky

  • The most likely way for the world to be destroyed, most experts agree, is by
  • accident. That's where we come in; we're computer professionals. We cause

accidents.

– Nathaniel Borenstein

  • Well, thanks to the Internet, I'm now bored with sex.

– Philip J. Fry

  • The Internet? Is that thing still around?

– Homer Simpson

  • The ability to quote is a serviceable substitute for wit.

  • W. Somerset Maugham
slide-4
SLIDE 4

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

slide-5
SLIDE 5

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
slide-6
SLIDE 6

That horse was askin’ for it!

slide-7
SLIDE 7

Oh the humanity!

slide-8
SLIDE 8

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)

slide-9
SLIDE 9

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

slide-10
SLIDE 10

Another RIA

slide-11
SLIDE 11

One more for good measure

slide-12
SLIDE 12

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
slide-13
SLIDE 13

Build or buy?

slide-14
SLIDE 14

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
slide-15
SLIDE 15

RPC mystified

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

slide-16
SLIDE 16

DWR’s brand of RPC

slide-17
SLIDE 17

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
  • utbound data
  • Handles instantiation and calling appropriate

methods of the server-side object

  • Transparently handles all that icky Ajax stuff
slide-18
SLIDE 18

DWR In a nutshell

slide-19
SLIDE 19

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>

slide-20
SLIDE 20

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>

slide-21
SLIDE 21

The basics, part 2a

slide-22
SLIDE 22

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

slide-23
SLIDE 23

The basics, part 4

Some client-side code to call it:

slide-24
SLIDE 24

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

slide-25
SLIDE 25

Mmmm… pudding… AGHHAGGAGHAGAGA

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

math

slide-26
SLIDE 26

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
slide-27
SLIDE 27

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

slide-28
SLIDE 28

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

slide-29
SLIDE 29

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

slide-30
SLIDE 30

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

debugging

slide-31
SLIDE 31

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!
slide-32
SLIDE 32

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

slide-33
SLIDE 33

Threats aren’t always as cute as this

slide-34
SLIDE 34

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

slide-35
SLIDE 35

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>

slide-36
SLIDE 36

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
slide-37
SLIDE 37

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
slide-38
SLIDE 38

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

slide-39
SLIDE 39

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

slide-40
SLIDE 40

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

slide-41
SLIDE 41

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>

slide-42
SLIDE 42

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

Here thar be JavaScript too:

slide-43
SLIDE 43

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

slide-44
SLIDE 44

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

account

slide-45
SLIDE 45
slide-46
SLIDE 46
slide-47
SLIDE 47

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
slide-48
SLIDE 48

I hate it when a plan doesn’t come together

slide-49
SLIDE 49

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…
slide-50
SLIDE 50

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

slide-51
SLIDE 51

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

slide-52
SLIDE 52

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

error

slide-53
SLIDE 53

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

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

slide-54
SLIDE 54

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)

slide-55
SLIDE 55

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

slide-56
SLIDE 56

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
slide-57
SLIDE 57

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>

slide-58
SLIDE 58

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

alltogether

slide-59
SLIDE 59

When forward isn’t cool enough: Reverse Ajax

slide-60
SLIDE 60

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…
slide-61
SLIDE 61

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

slide-62
SLIDE 62

The three horsemen of the apocalypse, part 2: Polling

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

slide-63
SLIDE 63

Introducing New and Improved Comet!

(WARNING!! TRADEMARK INFRINGEMENT ALERT!!)

slide-64
SLIDE 64

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)

slide-65
SLIDE 65

The mechanics of reverse Ajax: Piggybacking

  • To activate piggybacking:

DO NOTHING!

  • You can automatically piggyback on any

incoming request

slide-66
SLIDE 66

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>

slide-67
SLIDE 67

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>

slide-68
SLIDE 68

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

slide-69
SLIDE 69

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.

slide-70
SLIDE 70

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?

slide-71
SLIDE 71

(except in the case of UFOs apparently)

ra_*

slide-72
SLIDE 72
slide-73
SLIDE 73

Binary files

  • Can handle binary files (up and down)
  • Can deal with byte[], java.awt.BufferedImage,

java.io.inputStream or

  • rg.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:
slide-74
SLIDE 74

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

slide-75
SLIDE 75

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”);
slide-76
SLIDE 76

Varargs

  • Avoids wrapping arguments in an array, collection
  • r 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”);

slide-77
SLIDE 77

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

slide-78
SLIDE 78

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)

slide-79
SLIDE 79

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

slide-80
SLIDE 80

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

slide-81
SLIDE 81

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!

slide-82
SLIDE 82

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

slide-83
SLIDE 83

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

Download code and slide deck here: www.zammetti.com/philly_jug_dwr_presentation.zip