Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 37 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
37
Dung lượng
683,04 KB
Nội dung
131Incorporating applets with GWT available at http://fjax.net. However, this still will not allow you to call Amazon’s SOAP API 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 imple- 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 132 CHAPTER 5 Other Techniques for Talking to Servers and 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 SOAP service via the LiveConnect/NPAPI implementation. The main drawbacks include the general fragility of the Java applet plugin, the out- 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. PROBLEM Our client needs to access SOAP services, but GWT and web browsers do not inher- ently provide consistent access. SOLUTION You can create a robust SOAP client that can be used directly within a GWT client application by wiring together the communications through a Java applet. We’ll start by creating our applet. Because we’re using NetBeans for this project, creating the web service client is remarkably easy. The first step is to create a web ser- vice reference in the NetBeans project. This is done by selecting File > New from the menus, and selecting Web Services > Web Service Client from the New File dialog box, 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 133Incorporating applets with GWT After 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 estab- 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. package com.manning.gwtip.servercom.applet; import com.manning.gwtip.servercom.soap.HelloService; import com.manning.gwtip.servercom.soap .HelloServicePortType; import com.manning.gwtip.servercom.soap.Person; import java.applet.Applet; import netscape.javascript.JSObject; public class SOAPApplet extends Applet { public SOAPApplet() { super(); } 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 auto- generated classes from JAX-WS b Import JSObject from plugin.jar c 134 CHAPTER 5 Other Techniques for Talking to Servers window.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(); p.setFirstName((String) person.getMember("firstName")); p.setLastName((String) person.getMember("lastName")); String result = service.sayHello(p); return result; } } In our applet code, we’re making use of the autogenerated JAX-WS client-related classes, created here through NetBeans, to communicate with the remote service b . In addition, we’re using the JSObject type from plugin.jar to perform JavaScript- related tasks c . In spite of the simplicity and brevity of this class, you have likely already noticed that this is not nearly as generic an implementation as the Flash example. Since Java is a static language, we can’t as easily create a “universal” client that doesn’t involve com- plex building of each individual SOAP call. You’ll also notice that the sayHello() method doesn’t take any arguments, but rather fetches the value from a member on the Window object d . Unfortunately, the Java plugin doesn’t like taking arguments to externally exposed methods, even those taking JSObject . To get around this, we’ll have to make our calls by setting attributes on the Window object. 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; } private SOAPClientApplet() { this.appletObject = DOM.createElement("applet"); DOM.setAttribute(this.appletObject, "id", "SOAPClientApplet"); DOM.setAttribute(this.appletObject, "MAYSCRIPT", "true"); DOM.setAttribute(this.appletObject, "code", Listing 5.15 GWT class to talk to the applet Define sayHello() method using JSObject d Truncated for line length 135Incorporating applets with GWT "com.manning.gwtip.servercom.applet.SOAPApplet"); DOM.setAttribute(this.appletObject, "type", "application/x-java-applet;version=1.5.0"); DOM.setAttribute(this.appletObject, "pluginspage", "http://java.sun.com/j2se/1.5.0/download.html"); DOM.setAttribute(this.appletObject, "archive", JAX_WS_JARS+",GWTIP_Chapter5_Applet.jar"); DOM.setAttribute(this.appletObject, "cache_archive", JAX_WS_JARS ); DOM.setAttribute(this.appletObject, "width", "0px"); DOM.setAttribute(this.appletObject, "height", "0px" ); DOM.setAttribute(this.appletObject, "codebase", GWT.getModuleBaseURL()+"/applet/"); } public static void initialize(ReadyCallback callback) { SOAPClientApplet.getInstance(); INSTANCE.intializeJavaScript(callback); } private native Element intializeJavaScript(ReadyCallback callback)/*-{ $wnd.appletSOAPClientReadyNotify = function(){ callback. @[ ]SOAPClientApplet.ReadyCallback::onReady()(); } }-*/; public static interface ReadyCallback { public void onReady(); } public native String sayHello( JavaScriptObject person)/*-{ $wnd.person = person; return $wnd.document.getElementById("SOAPClientApplet").sayHello(); }-*/; } Here we’re recycling a number of concepts from the Flash version of our SOAP client. DISCUSSION The ReadyCallback works the same way it did in the Flash example, this time invoked from the applet’s start() method. We use an <applet> tag to instantiate the applet, and the cache_archive attribute to keep users from having to download the entire JAX-WS distribution every time they visit the page b . TIP The deprecated <applet> tag is used in listing 5.15. While it’s recom- 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. Set cache_archive b Package name replaced with […] Define service method c 136 CHAPTER 5 Other Techniques for Talking to Servers We 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. PROBLEM The applet SOAP client won’t talk to the server because they are of different origins. SOLUTION By creating a trusted applet (using a trusted security certificate), we can enable com- munications with any SOAP resource, regardless of its origin. We’ll create a trusted applet by using a certificate. For the purposes of this exam- ple, we’ll simply create a self-signed certificate. Listing 5.16 shows a shell session dem- onstrating how to use keytool and jarsigner to self-sign the applet’s JAR. $ keytool -genkey -alias gwtip -keyalg rsa Enter keystore password: googlewebtoolkit What is your first and last name? [Unknown]: GWT What is the name of your organizational unit? [Unknown]: Manning What is the name of your organization? [Unknown]: Manning What is the name of your City or Locality? [Unknown]: Atlanta What is the name of your State or Province? [Unknown]: Georgia What is the two-letter country code for this unit? [Unknown]: US Is CN=GWT, OU=Manning, O=Manning, L=Atlanta, ST=Georgia, C=US correct? [no]: yes 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 137Streaming to the browser with Comet Notice that you need to repeat the last step, using jarsigner , for each of the depen- 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. DISCUSSION Once the applet has been approved, it can call foreign servers. You can also use this technique to access files on the user’s filesystem for local persistent storage. The applet, as a client, does provide for synchronous calls like the other remote services we have looked at so far. However, it is a request-response type of communica- tion with the server. There are other ways to provide streaming data from the server, and while they can also be done with an applet provider, we’ll move on to a pure JavaScript technique called Comet. 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 138 CHAPTER 5 Other Techniques for Talking to Servers PROBLEM Our application needs live server events to be sent to the client, rather than relying on a request-response cycle. SOLUTION We will push data to the client using the Comet technique, rather than waiting for the client to request data. By keeping a hidden <iframe> on the page connected to the server and continu- ously loading, you can send messages from the server to the client in near-real time. While Comet is a technique, more than a library, we’ll be looking at a specific imple- mentation for GWT created by Luca Masini and available for download at http:// jroller.com/masini/entry/updated_comet_implementation_for_gwt. We’ll cover the basics of this concept in this chapter, and in chapter 11 we’ll build an enhanced ver- sion of this implementation to support some additional features. There are three parts to this Comet implementation that we’ll focus on here: the cli- ent-side GWT message receiver, the server-side servlet that brokers messages to the Comet stream, and the regular GWT RPC service for sending messages. This last part is a regular GWT RPC 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. 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) { restartStreamingFromIFrame(); callback("restartStreaming", (String)result); } }; Listing 5.17 Streaming message receiver class 139Streaming to the browser with Comet private 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(); subScribeToEvent((String)callback.getKey(), (AsyncCallback)callback.getValue()); iter.remove(); } callback("keepAlive",""); } }; public static StreamingService getInstance() { if(instance == null) { instance = new StreamingServiceGWTClientImpl(); } return instance; } private StreamingServiceGWTClientImpl() { callbacks.put("keepAliveInternal", internalKeepAliveCallback); callbacks.put("restartStreamingInternal", restartStreamingCallback); ((ServiceDefTarget) service).setServiceEntryPoint( GWT.getModuleBaseURL()+ "streamingService"); setUpNativeCode(this); restartStreamingFromIFrame(); createWatchDogTimer(); } private void callback( String topicName, String data) { keepAlive = true; if(callbacks.containsKey(topicName)) { AsyncCallback callback = (AsyncCallback)callbacks.get(topicName); try { Object dataToSend = data; if(data.startsWith("$JSONSTART$") && data.endsWith("$JSONEND$")) { Define constructor with callbacks b Pass message to user’s callback c 140 CHAPTER 5 Other Techniques for Talking to Servers dataToSend = JSONParser.parse( data.substring( "$JSONSTART$".length(), data.length()-"$JSONEND$".length())); } callback.onSuccess(dataToSend); } catch (JSONException e) { callback.onFailure(e); } } } private native void setUpNativeCode( StreamingService thisInstance) /*-{ $wnd.callback = function(topicName, data) { thisInstance. @org.gwtcomet.client.StreamingServiceGWTClientImpl ::callback (Ljava/lang/String;Ljava/lang/String;) (topicName,data); } }-*/; private void createWatchDogTimer() { Timer t = new Timer() { public void run() { if(!keepAlive) { restartStreamingFromIFrame(); } keepAlive = false; } }; t.scheduleRepeating(watchDogTimerTime); } private void restartStreamingFromIFrame() { Element iframe = DOM.getElementById("__gwt_streamingFrame"); if(iframe!=null) { DOM.removeChild(RootPanel.getBodyElement(), iframe); } iframe = DOM.createIFrame(); DOM.setAttribute(iframe, "id", "__gwt_streamingFrame"); DOM.setStyleAttribute(iframe, "width", "0"); DOM.setStyleAttribute(iframe, "height", "0"); DOM.setStyleAttribute(iframe, "border", "0"); DOM.appendChild(RootPanel.getBodyElement(), iframe); DOM.setAttribute(iframe, "src", streamingServicePath); } public void sendMessage(String topicName, String data) { service.sendMessage(topicName, data, voidCallback); } Create native callbacks to call GWT class d [...]... module in the code, we need to inherit the module in the gwt. xml file as we would a Java module Once the elements are processed, GWT. .. Toolkit settings option in the IntelliJ IDEA settings panel This setting can be used to specify your GWT Home folder 154 CHAPTER 6 Integrating Legacy and Third-Party Ajax Libraries in your module’s gwt. xml file This is an incredibly useful feature if you’re dealing with multiple dependencies for packaged JSNI scripts, which we’ll look at in this chapter The second feature is the syntax highlighting, which... As we discussed in chapter 2, the tag in your module definition takes care of this by allowing a module definition to include a JavaScript file in the public package of the module, and it tells the gwt. js file to load that script before the rest of the module starts executing Since the public directory will be included in any modules that inherit from the original, the loading of the script... out of existence 162 CHAPTER 6 Integrating Legacy and Third-Party Ajax Libraries elements in the JavaScript libraries In the next section, we’ll look in more detail at maintaining interactions between GWT components and JavaScript libraries This is a pattern you’ll find useful to repeat when building JSNI wrappers for many JavaScript libraries 6.3 Managing GWT- JavaScript interaction Script.aculo.us (http://script.aculo.us)... between DOM element references and GWT classes, you will need to maintain lookup structures As mentioned in the previous section, this is a pattern you’ll find useful in any JSNI wrapper you’re creating PROBLEM Our JavaScript library is DOM Element-based, but your GWT code depends on UIObject 163 Managing GWT- JavaScript interaction Figure 6 .5 Dragging the image around in the window creates a ghost image... arguments in the methods at the beginning of the DragAndDrop class in listing 6.7 c, but they’re mostly straightforward Passing a UIObject to setDraggable() makes the UIObject draggable Passing it to unsetDraggable() disables dragging Each of the arguments is explained in table 6.2 Managing GWT- JavaScript interaction 1 65 Table 6.2 Arguments for setDraggable() These are part of the associative array in JavaScript... chapter, for use with GWT 6.1.3 Configuring IntelliJ IDEA It’s not coincidental that this chapter on integrating JavaScript using JSNI is the one in which we’re introducing IntelliJ IDEA as the spotlight tool Of all the IDEs, IDEA’s core GWT support is exceptionally good While it’s not a free option like Eclipse or NetBeans, it’s a fantastic development environment For GWT applications, it includes a lot... browser with Comet Listing 5. 19 Business service class public class StreamingServiceBusiness { private final static long serialVersionUID = 1; private final ConcurrentMap queues = new ConcurrentHashMap(); private final Observable observable = new EventObservable(); public StreamingServiceBusiness() { super(); } public... SOLUTION We’ll start by creating the setDraggable() and unsetDraggable() methods and their supporting code, as shown in listing 6.7 This supporting code maintains lookups between Elements and UIObjects Listing 6.7 The first portion of the DragAndDrop class public class DragAndDrop { public static final String CONSTRAINT_VERTICAL="vertical"; public static final String CONSTRAINT_HORIZONTAL="horizontal";... zIndex, constraint, Save JavaScriptObject ghosting); to map draggables.put(source, draggable); } private static native JavaScriptObject setDraggable( JavaScriptObject element, JavaScriptObject handle, boolean revert, int zIndex, String constraint, boolean ghosting )/*-{ return new $wnd.Draggable( element, { handle : handle, revert : revert, zindex : zIndex, constraint: constraint, ghosting: ghosting . "code", Listing 5. 15 GWT class to talk to the applet Define sayHello() method using JSObject d Truncated for line length 135Incorporating applets with GWT "com.manning.gwtip.servercom.applet.SOAPApplet"); . 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,. streamingServicePath = GWT. getModuleBaseURL()+ "streamingServlet"; private static StreamingService instance = null; private final StreamingServiceInternalGWTAsync service = (StreamingServiceInternalGWTAsync )GWT. create(