Once an applet has been signed with a certificate from a trusted authority, it can access all the features of Java, including talking to the local filesystem and communicating with any s
Trang 1available at http://fjax.net However, this still will not allow you to call Amazon’s SOAPAPI at will.
5.4.3 Drawbacks and caveats
Obviously, getting around the same-origin policy limitation of SOAP doesn’t mean you can access any SOAP server on the Internet You can only access servers that have a crossdomain.xml file defined that allows your host
There are some other drawbacks as well The big one is that the Flash SOAP mentation is fairly fragile If your SOAP services are robust and are already known to work across multiple platforms (say, NET, Axis, XFire, and PHP), then you’ll likely not run into any problems However, the Flash SOAP support is not very good at dealing with complex namespaces, and creating a SOAP service with the Glassfish JSR-181/JAX-WS implementation, for example, yields a service that you can’t successfully call with Flash (This is why the sample service included with the downloadable code for this chapter is built with XFire.)
You should also be aware that the ExternalInterface class is only available with Flash 8 This means it will not work with older Flash versions, including the produc-tion version for Linux right now, though there is a beta of a newer Flash for Linux available As a workaround, you can use the ExternalInterface implementation available with the Dojo Ajax library This implementation allows you to bundle a Flash 6 and Flash 8 movie with your application, and switch between them depending on which version of Flash is available on the client More information on this is available
at http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book50
Wouldn’t it be nice if you could access anything on the web from your application and have a SOAP client you knew was good, without a great deal of pain? Fortunately, there is another option in the series of workarounds to this problem: replacing the Flash layer with an applet
5.5 Incorporating applets with GWT
The Java applet has been around for over a decade, and although it has never seen the adoption that some hoped for, it is still one of the most powerful web-embeddable frameworks around One of the features that makes this so is the applet security model While the default behavior for an applet is not unlike the same-origin policy, it pro-vides additional options through the signed applet policy Once an applet has been signed with a certificate from a trusted authority, it can access all the features of Java, including talking to the local filesystem and communicating with any server We’ll look
at building a GWT-accessible SOAP client with an applet, using the JAX-WS reference implementation from Sun and making a call to the Hello service from listing 5.11
5.5.1 Using Java as a SOAP client
We’re once again addressing the problem of getting access to SOAP services from a GWT client, but this time we’ll be using a Java applet Applets have some advantages
Trang 2and disadvantages In the plus column, they are much more powerful than Flash, you can ensure that the SOAP client will work with any server out there much more easily, and, perhaps most importantly, you can actually make synchronous calls to the SOAPservice via the LiveConnect/NPAPI implementation
The main drawbacks include the general fragility of the Java applet plugin, the of-browser execution, and the lack of penetration among the general installed browser user base Of course, if your application is part of an internal enterprise envi-ronment, this latter point is less important
as shown in figure 5.4
In the New Web Service Client dialog box in NetBeans, shown in Figure 5.5, you’re prompted to enter additional information Enter the URL for your deployed Hello-Service WSDL file and specify a package name for the generated client classes to be placed into
Figure 5.4 Creating a new web service client in NetBeans
Trang 3After you have the web service reference added to your project, do an initial build This will cause the Ant script of the project to generate the appropriate classes and place them in the build/generated folder of your project, making them available to your editor’s autocomplete features.
Next we need to build the applet that will use the web service client we have lished This will be a remarkably simple class, but there are a few things worth noting First, we’re going to be using the netscape.javascript.* package Despite the name, this is generally well supported in most browsers, including IE and Safari However, you do need to add the JAR file to the build path of your project It can be found in your [JAVA_HOME]/jre/lib/ folder as plugin.jar Listing 5.14 shows our applet
public void start() {
JSObject window = JSObject.getWindow(this);
Listing 5.14 SOAP client applet
Figure 5.5 Setting WSDL and client configuration information for the client
Import generated classes from JAX-WS
auto-b
Import JSObject from plugin.jar
c
Trang 4window.eval("appletSOAPClientReadyNotify();");
}
public String sayHello() {
HelloService client = new HelloService();
HelloServicePortType service = client.getHelloServiceHttpPort(); JSObject window = JSObject.getWindow(this);
JSObject person = (JSObject) window.getMember("person");
Person p = new Person();
We’ll copy the built applet into the public.applet package of our GWT project, along with its dependencies Now we need to create our GWT class that will talk to the applet As shown in listing 5.15, this is much simpler than the Flash version, but less generic
public class SOAPClientApplet {
private static final String
JAX_WS_JARS="activation.jar,
private static SOAPClientApplet INSTANCE;
private Element appletObject;
public static SOAPClientApplet getInstance() {
INSTANCE = INSTANCE == null ? new SOAPClientApplet() : INSTANCE; return INSTANCE;
d
Truncated for line length
Trang 5public static interface ReadyCallback {
public void onReady();
TIP The deprecated <applet> tag is used in listing 5.15 While it’s mended that you use an <object><embed></object> construction for Java applets, as was used in the Flash example in listing 5.10, we have found that using LiveConnect in this configuration can be problematic.The big difference between the applet and Flash methods is the hand-coded service method in this example c While we’re just returning a simple string here, you could return a JavaScriptObject and use the JavaScriptObjectDecorator to access it
recom-Set cache_archive
b
Package name replaced with […]
Define service method
c
Trang 6We now have a SOAP client, but once again have the same-origin policy security issue to deal with.
5.5.2 Signing JARs for security bypass
The Java security model allows for trusted applets to have access to the full features of the JRE inside a browser Applets that are not trusted are subject to the same-origin policy and other security constraints
Trusted applets are signed with a certificate from a trusted certificate authority For publicly deployed applications, you should acquire a certificate from a public service such as Thawte or VeriSign For internal use or examples, such as this book, you can create your own self-signed certificate
exam-$ keytool -genkey -alias gwtip -keyalg rsa
Enter keystore password: googlewebtoolkit
What is your first and last name?
Enter key password for <gwtip>
(RETURN if same as keystore password):
$ keytool -selfcert -alias gwtip
Enter keystore password: googlewebtoolkit
$ jarsigner GWTIP_Chapter5_Applet.jar gwtip
Enter Passphrase for keystore: googlewebtoolkit
Warning: The signer certificate will expire within six months.
Listing 5.16 Using keytool and jarsigner
Repeat this step with all dependency JARs
b
Trang 7Notice that you need to repeat the last step, using jarsigner, for each of the dencies b Each JAR will have its own security context, and if one of them isn’t signed and attempts a security-restricted operation, such as talking to a non-origin server, you’ll get a security exception
Once you have successfully deployed the signed applet, its invocation causes Java to prompt the user to trust the applet’s signatory within the JRE, as shown in figure 5.6
5.6 Streaming to the browser with Comet
Ajax Comet get it? Well, OK, it’s a moderately cute name at best However, as a means of providing low-latency event type data to an application, such as Meebo com’s browser-based instant messaging client, Comet is pretty cool
Comet takes advantage of how modern web browsers load pages: inline JavaScript
is executed as it’s parsed by the browser, rather than once the whole page has loaded This behavior can confuse new DHTML developers, because calling an object declared farther down the page from the current script execution may result in JavaScript errors being thrown However, it provides a way to send small executable messages to the client because the TCP stream serving the page never completely closes
If you’re old enough in web terms, you might remember the old technique of server-push animation In this technique, a stream serving an image file was never closed, and updated images were incrementally pushed to the browser Each new image would clobber the previous image, and it would appear as though the browser were slowly downloading an animated GIF file or playing a movie Comet uses a very similar concept
Figure 5.6
A prompt to trust the signed applet
Trang 8There are three parts to this Comet implementation that we’ll focus on here: the ent-side GWT message receiver, the server-side servlet that brokers messages to the Comet stream, and the regular GWTRPC service for sending messages This last part is a regular GWTRPC service with some additional code to create the streaming connection frame and register a callback method (using JSNI) to handle messages from that frame Listing 5.17 shows the code for the first part—the client message receiver
cli-public class StreamingServiceGWTClientImpl implements StreamingService {
private int watchDogTimerTime = 100000;
Map callbacks = new HashMap();
private boolean keepAlive = false;
private final String streamingServicePath =
GWT.getModuleBaseURL()+ "streamingServlet";
private static StreamingService instance = null;
private final StreamingServiceInternalGWTAsync service =
(StreamingServiceInternalGWTAsync)GWT.create(
StreamingServiceInternalGWT.class);
private final Map waitingSubscriber = new HashMap();
private final static AsyncCallback voidCallback =
new AsyncCallback() {
public void onFailure(Throwable caught) {}
public void onSuccess(Object result) {}
};
private final AsyncCallback restartStreamingCallback =
new AsyncCallback() {
public void onFailure(Throwable caught) {}
public void onSuccess(Object result) {
Trang 9private final AsyncCallback internalKeepAliveCallback =
new AsyncCallback() {
public void onFailure(Throwable caught) {}
public void onSuccess(Object result) {
alert("keepAlive");
keepAlive = true;
watchDogTimerTime = 10*Integer.parseInt(result.toString()); for(Iterator iter =
waitingSubscriber.entrySet().iterator();iter.hasNext();) {
Entry callback = (Entry)iter.next();
restartStreamingCallback);
((ServiceDefTarget) service).setServiceEntryPoint(
GWT.getModuleBaseURL()+ "streamingService"); setUpNativeCode(this);
restartStreamingFromIFrame();
createWatchDogTimer();
}
private void callback(
String topicName, String data) {
b
Pass message to user’s callback
c
Trang 10dataToSend = JSONParser.parse(
data.substring(
"$JSONSTART$".length(),
data.length()-"$JSONEND$".length())); }
private void createWatchDogTimer() {
Timer t = new Timer() {
public void run() {
private void restartStreamingFromIFrame() {
Element iframe = DOM.getElementById(" gwt_streamingFrame"); if(iframe!=null) {
public void sendMessage(String topicName, String data) {
service.sendMessage(topicName, data, voidCallback);
Create native callbacks
to call GWT class
d
Trang 11public void sendMessage(String topicName, JSONValue object) {
"' is cached with callback "+
callback+" until online");
to pass data back into the GWT application d.
The callback is fired by <script> tags served incrementally by the server These scripts invoke the JavaScript callback method on the window object The messages themselves come from a servlet using an observable to notify clients Our example servlet for this task is shown in listing 5.18
public class StreamingServlet extends RemoteServiceServlet {
private final static long serialVersionUID = 1;
private final static byte[] PAYLOADCONTENT = "//payload\n".getBytes(); private long keepAliveTimeout = 10000;
private long numberOfIteration = 20;
private int payload = 500%PAYLOADCONTENT.length;
private int counter = 0;
Trang 12String numberOfIterationString = getServletConfig()
d
Begin stream cycle
e
Trang 13final HttpSession session = request.getSession();
" starting streaming for client "+session.getId());
Observer updatesObserver = new Observer() {
public void update(Observable o, Object arg) {
StreamingServiceBusiness.Event event =
(StreamingServiceBusiness.Event)arg;
if(StreamingServiceImpl.getStreamingServiceBusiness() .isClientSubscribedToQueue(
StringBuffer stream = new StringBuffer();
for (Iterator<StreamingServiceBusiness.Event> iter = eventList.iterator();
iter.hasNext();) {
StreamingServiceBusiness.Event event =
iter.next();
if((System.currentTimeMillis()-event.eventTime) <
to watch events
f
Loop for fixed number
of iterations
g
Trang 14f, and begins a loop of a fixed number of iterations g Each of these represents a burst of messages sent to the client as script elements c The servlet then sends any pending messages, or, if there are none, it sends a keep-alive event to keep the socket open b Once the messages are flushed, the servlet calls waitForEvents() on the ser-vice, which blocks the thread’s execution until there are more events needing to be sent h Finally, once the number of iterations has been reached, the servlet sends the client a message to restart the stream connection i
This last step, restarting the connection, is important for a number of reasons First, remember that all of this is being written to an <iframe> in the client browser, which means it’s a document held in memory This is true even if the events fired by the callback are transient in the GWT application This means that a communication frame is expanding in your browser’s memory, and it should be cleaned up from time
to time Such housekeeping also helps with browsers that get irritable on long socket reads Some browsers timeout a socket read if the page rendering starts taking too long; resetting it from time to time prevents this
You’ll also notice the use of URLEncoder and unescape() paired with each event sent This ensures that special characters that might otherwise break the JavaScript string are properly handled, without a complex replacement at call time The final value is then passed to the window.parent.callback() method, which was created in the previous listing, and which takes the data and passes it through to the GWT code Now that we have seen it referenced a few times, it’s time to look at the business service, which is shown in listing 5.19
Block thread execution waiting for events
h
Tell client to restart stream
i
Trang 15public class StreamingServiceBusiness {
private final static long serialVersionUID = 1;
private final ConcurrentMap<String,
public static class EventObservable extends Observable {
public synchronized void notifyObservers(Object arg) {
public boolean isClientSubscribedToQueue(
String topicName, String clientName) {
return queues.containsKey(topicName)
&& queues.get(topicName).containsKey(clientName);
}
public static class Event {
public final String queueName;
public final String message;
public final long eventTime = System.currentTimeMillis();
public Event(String queueName, String message) {
c
Trang 16public String toString() {
}
}
Here, Luca is keeping a set of ConcurrentHashMap objects to store the subscription state of a user to a particular queue Once a message comes in, it’s sent to the observ-able and the servlet determines whether a client should receive it The servlet can determine this, of course, based on which clients have subscribed b (and clients may also unsubscribe c) The clients can then block their loop d as you saw in the servlet (listing 5.18) on the waitForEvents() method If the keep-alive timeout is reached before a message is received, the servlet’s thread will resume and it will send the keep-alive message
Block thread
d
Trang 175.7 Summary
We have given you a fairly exhaustive tour of the various ways to communicate with servers from GWT While you’re unlikely to use all of the techniques in every applica-tion, this discussion has presented several browser technologies and techniques avail-able for use with GWT
We examined the security issues surrounding browser-server communications, as well as several techniques for communicating with servers and working around related limitations We either discussed or demonstrated the use of POX, REST, and SOAP for communication, and we looked at using Flash and Java applets to enhance
or enable certain aspects of the process Table 5.2 summarizes the server cations technologies we discussed, and the related capabilities and methods involved with using each
communi-Related to our more general discussion of server communications, we also covered the Comet technique for sending stream/event type data to the client from the server We’ll take a deeper look at this technique in chapter 11, as it forms the basis of the large practical application presented there
In addition to all of these concepts and technologies, we took a brief look at one of the options for developing GWT applications in the NetBeans IDE (highlighting another tool available to developers) So far in this book, we have used the Google Application-Creator and ProjectCreator utilities, the Eclipse IDE, and the NetBeans IDE
In the next chapter, we’ll explore IntelliJ IDEAIDE support for GWT while we take
a closer look at JSNI While many of the examples presented in this chapter made some use of it, we’ll take a look at the full functionality of JSNI and at some JavaScript libraries that might be useful to integrate with your GWT applications
Table 5.2 Capabilities of server communication methods
Method Origin server Other servers Methods
Trang 18We introduced JSNI in chapter 1 and used it in several other examples in this book, so you should already have a handle on the basics Though JSNI is very pow-erful, it can also be confusing and problematic When working with JSNI, you have
to keep in mind that you’re on your own, and your own feet are in range of your
This chapter covers
■ Working with the JavaScript Native Interface
■ Creating JSNI Wrappers
■ Dealing with JavaScript eventing models
■ Working with the JSIO project