If you took a look at my previous post GWT and NetBeans you should know how to set up the bare minimum for a GWT project in NetBeans. In this post, we are going to see how to consume a free online web service LyricsWikia from our GWT project. The SOAP based LyricsWikia web service allows you to search their database to return song lyrics for a given song title, album or music artist.

You can use another web service of your choice instead of LyricsWikia if you prefer, just do some googling for free web services. Some examples are Amazon (AWS), weather services, YouTube, post-code/location finders etc.

Normally when I consume web services in NetBeans, I go for JAX-WS style because it is the successor to JAX-RPC. Unfortunately in this case the WSDL we are going to use is old and is an RPC style one. So to accomodate this you need to do the following:

Open NetBeans (I’m using 6.8) and select Tools –> Plugins then navigate to the Available Plugins tab. Check JAX-RPC Web Services and install. We’ll get back to the web service later, for now we need to set up the project. If you haven’t done so already, you need to create a GWT project in NetBeans, see this post GWT and NetBeans for how to set up a basic project. The first thing we are going to do to extend this project is create a GWT RPC Service. Using this architecture the GWT client side code will call the GWT server side code using asynchronous RPC calls. The GWT server side code will act as a web service client and consume the web service, then it will return the results to our GWT client side code for presentation in the browser.

In your NetBeans project go to New –> GWT RPC Service… and make the service name something really original like GwtRpcService. Make sure the Create Usage Example Class checkbox is checked, this will make our life easier. Next, have a look at the generated classes. In the com.client package you should now have GWTRPCService.java, GWTRPCServiceAsync.java and GWTRPCServiceUsageExample.java. In the com.server package you should now have GWTRPCServiceImpl.java. An RPC service method has been created for us myMethod(String s) we will modify this method to something more suitable for our requirements.

Open GWTRPCService.java and replace:

public String myMethod(String s);
with:
public String getLyrics(String artist, String song);

Open GWTRPCServiceAsync.java and replace:

public void myMethod(String s, AsyncCallback<String> callback);

with:

public void getLyrics(String artist, String song, AsyncCallback<String> callback);

Open GWTRPCServiceImpl.java and replace:

public String myMethod(String s) {
    // Do something interesting with 's' here on the server.
    return "Server says: " + s;
}

with:

public String getLyrics(String artist, String song)
{
    return null;
}

I’m not going to go into great detail about what we just did because you can google that yourself, basically we have an RPC service method getLyrics(…) and we will be calling this method asynchronously. In the implementation class we don’t just want to return null, we want to call a suitable LyricsWikia web service operation and return the result.

OK next problem. The LyricsWikia WSDL will not work out of the box for us Java heads. You might think, oh great example. But this is reality. In my experience this happens a lot and small changes need to be made to get things working in your preferred IDE – or more specifically whatever you are using to generate client stubs. To overcome this, we need to download the WSDL to our local machine and make a couple of changes then use this local version to generate our client stubs instead of the URL for the deployed WSDL.

Get the LyricsWikia WSDL here. If you are are using Safari like me and you browse to this you might think this doesn’t look like a WSDL. It must be the way Safari renders WSDL files or something but I had to go View –> View Source then Select AllCopy and Paste the selection into a text editor and Save it. I saved the file as LyricsWikia.wsdl.

Open LyricsWikia.wsdl and replace:

<xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="AlbumResult[]"/>

with:

<xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="tns:AlbumResult[]"/>

Replace:

<xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="AlbumData[]"/>

with:

<xsd:attribute ref="SOAP-ENC:arrayType" wsdl:arrayType="tns:AlbumData[]"/>

Save the file.

In the NetBeans project go to New –> Web Service Client… Select Local File and browse to the local copy of the WSDL that we just edited. Specify a Package name, I will use com.lyricsWikia. For this particular web service, the client style must be JAX-RPC Style. Select Finish.

In NetBeans 6.8, if I try and build the project at this point, I will see the following error message:

../nbproject/build-impl.xml:422: taskdef class com.sun.xml.rpc.tools.ant.Wscompile cannot be found
BUILD FAILED (total time: 0 seconds)

Well that is not cool. To fix it, in the NetBeans Files view locate your projects root folder –> nbproject folder –> project.properties and right click Edit.

Replace:

wscompile.classpath=${wscompile.tools.classpath}:${j2ee.platform.wscompile.classpath}

with:

wscompile.classpath=${wscompile.tools.classpath}:${j2ee.platform.wscompile.classpath}:${javac.classpath}

If you open GWTRPCServiceUsageExample.java and comment out the following line of code you should now be able to build the project successfully:

getService().myMethod(txtUserInput.getText(), callback);

It’s now time to implement our getLyrics(…) method now that we have generated our web service client stubs (they should be visible under Generated Sources (jax-rpc)).

Open GWTRPCServiceImpl.java and replace:

public String getLyrics(String artist, String song)
{
      return null;
}

with:

public String getLyrics(String artist, String song)
{
      LyricWiki service = new LyricWiki_Impl();
      LyricWikiPortType port = null;
      String lyrics = "";
      try
      {
            port = service.getLyricWikiPort();
            ((Stub)port)._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, "http://lyrics.wikia.com/server.php");
      }
      catch(ServiceException ex)
      {
            ex.printStackTrace();
      }
      boolean exists = false;
      try
      {
            exists = port.checkSongExists(artist, song);
            if(exists)
            {
                  LyricsResult result = port.getSong(artist, song);
                  lyrics = result.getLyrics();
            }
            else
            {
                  lyrics = "Sorry, no lyrics available";
            }
      }
      catch(RemoteException ex)
      {
            ex.printStackTrace();
      }
      return lyrics;
}

You will also need to add the following import statements to the top of the class:

import com.lyricsWikia.LyricWiki;
import com.lyricsWikia.LyricWikiPortType;
import com.lyricsWikia.LyricWiki_Impl;
import com.lyricsWikia.LyricsResult;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.Stub;

We are nearly done. Now, we just need to modify our GWT client side code to call our RPC service method. We will create a very simple form that allows the user to pass in a music artist and a song title as a parameter and press a button to retrieve the lyrics.

Modify GWTRPCServiceUsageExample.java so it looks like this:

package com.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.TextArea;

/**
 * Example class using the GWTRPCService service.
 *
 * @author nes
 */
public class GWTRPCServiceUsageExample extends VerticalPanel {

    private TextArea txtAreaServerReply = new TextArea();
    private TextBox txtArtistInput = new TextBox();
    private TextBox txtSongInput = new TextBox();
    private Button btnSend = new Button("Get Lyrics!");

    public GWTRPCServiceUsageExample() {
        add(new Label("Music Artist: "));
        add(txtArtistInput);
        add(new Label("Song: "));
        add(txtSongInput);
        add(btnSend);
        txtAreaServerReply.setSize("500px", "500px");
        add(txtAreaServerReply);
        // Create an asynchronous callback to handle the result.
        final AsyncCallback<String> callback = new AsyncCallback<String>() {
            public void onSuccess(String result) {
                txtAreaServerReply.setText(result);
            }
            public void onFailure(Throwable caught) {
                txtAreaServerReply.setText("Communication failed");
            }
        };
        // Listen for the button clicks
        btnSend.addClickHandler(new ClickHandler(){
            public void onClick(ClickEvent event) {
                // Make remote call. Control flow will continue immediately and later
                // 'callback' will be invoked when the RPC completes.
                getService().getLyrics(txtArtistInput.getText(), txtSongInput.getText(), callback);
            }
        });
    }

    public static GWTRPCServiceAsync getService() {
        // Create the client proxy. Note that although you are creating the
        // service interface proper, you cast the result to the asynchronous
        // version of the interface. The cast is always safe because the
        // generated proxy implements the asynchronous interface automatically.
        return GWT.create(GWTRPCService.class);
    }

}

Modify codenesEntryPoint.java (or your entry point class) so it looks like this:

package com.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * Main entry point.
 *
 * @author nes
 */
public class testEntryPoint implements EntryPoint {

    /**
     * Creates a new instance of testEntryPoint
     */
    public testEntryPoint() {
    }

    /**
     * The entry point method, called automatically by loading a module
     * that declares an implementing class as an entry-point
     */
    public void onModuleLoad() {
        RootPanel.get().add(new GWTRPCServiceUsageExample());
    }

}

Now Build, Deploy and Run the project. If you are having any problems check your Glassfish server log for errors or debug the project in NetBeans. To debug the project in NetBeans, GWT Development Mode needs a browser plugin. You can also use Firebug (Mozilla plugin) to debug inside your browser.

Run the Project

Run the Project

Note. The LyricsWikia web service will only return a small portion of the song lyrics if they are available. This is due to licensing restrictions/fair use policy.