Create cooperating web services with Rational Application Developer V6

Developing web services that call each other is similar to developing a standalone application that calls a web service. But there are some important differences, especially in how tools, like IBM® Rational® Application Developer for WebSphere® software, Version 6.0 (Application Developer), are used to create them. This article will discuss design considerations and tool manipulations required to develop web services that call other web services. We'll walk through creation, deployment, and calling these web services. We'll also use the Web Services Navigator, available from IBM alphaWorks, to visualize their calling sequence within the WebSphere environment.

Share:

David Schmidt (schmidtd@us.ibm.com), Advisory Software Engineer, IBM

David Schmidt is a Senior Software Engineer at the IBM corporation. He has played design, development, test and quality assurance roles there since 1991. His recent involvement with the IBM alphaWorks Web Services Navigator launched him headlong into the Web services arena.



08 April 2005

Calling all web services

Calling a web service from within any Java™ program, whether from a Java bean, a standalone Java Application, or another web service, is exactly the same. You have the same decision to make about binding to the web service: creating a static client/stub or binding through a dynamic client using UDDI. In order to simplify the discussion and implementation, we will use the static stub generation. For situations where the interface won't be changing once defined or situations where one has complete control over both ends of the web service transaction, the static stub paradigm is suitable. For further discussion about client programming choices, see chapter 11 in WebSphere Version 6 Web Services Handbook Development and Deployment, also listed under Resources.

All of the development and deployment discussion in this article will apply equally well to the Application Developer environment, as well as the latest IBM WebSphere Application Developer environments, though the examples shown use Application Developer.


Design Considerations

In order to watch the web services calling each other using the alphaWorks Web Service Navigator, you need to ensure the web services conform to the JSR 109 standard. Further, since we are binding to our web services statically by using generated client stubs, that dictates some details about our calling convention.

Let's say you want to create three web services that are capable of calling one another. Call them Web1, Web2 and Web3, for example. That means their main implementations will be called Web1.java, Web2.java, and Web3.java in Application Developer. Application Developer will do the stub generation work, coming up with support classes automatically.

Implementation will be very simple. The web services will have one method signature, doWork(), which will take an integer as input and will return a string as output. The entire source to Web1.java looks like this (without doWork() really implemented yet):

Listing 1. A very simple web service
package com.mycompany.webservices.web1;

public class Web1
{
  public Web1()
  {
  }

  public String doWork(int work)
  {
    return "Web1 called with work: "+work;
  }
}

You can implement all three web services with this same skeleton. Each will be able to execute the doWork() method with the same inputs and outputs; we'll worry about differentiating the three outputs once all three web services and some support infrastructure are built.

Since you want the web services to call one another using JSR 109 rules, you can't just put all the web services into the same project and deploy them all in the same EAR, since we are using a static client design. A little further down the road, Application Developer will want to generate the client stub classes. When the client stub for Web1.java is created, Application Developer will create an interface for Web1.java and call it Web1.java, as well. If you try to generate that stub back into a single, consolidated project/EAR, it would overwrite the main class. Therefore, we're going to create three separate projects to house the three web services.


Building Web Services in Application Developer

Enabling web service creation in Application Developer

In order to develop web services, Application Developer needs to have the "Web Service Developer" role enabled. To check if it is enabled, select the Help>Welcome menu item. Check across the bottom of the screen for the "Earth-with-bell" icon. If it doesn't appear, click on the little person in the lower-right hand side of the welcome screen. Click on the "Earth-with-bell" icon from the list that pops up. That will enable the Web Service Developer role. Please see the example in Figure 1. You can close the welcome screen after you're done, and you should not have to repeat the process again (unless you create a new workspace).

Before getting started, ensure Application Developer has the "Web Service Developer" role enabled. Click on Help>Welcome from the Application Developer menu, and ensure you have an "Earth-with-bell" icon, as illustrated in Figure 1. If it does not appear, see the sidebar "Enabling web service creation in Application Developer".

Figure 1. Application Developer welcome screen with "Web Service Developer" role enabled
wsenable

Now that we have the skeleton web service and interface in mind, we will prepare three as web services. See the Resources for a "Project Interchange" zip file containing all the source and support already built. Use the RAD File>Import... Project Interchange function to import the completed projects directly into your Application Developer environment.

Step 1: Create a new "Dynamic Web Project" project. From the Application Developer File menu, select File>New>Dynamic Web Project. Name it Web1, and click the Finish button. Create Dynamic Web Projects Web2 and Web3 in the same way. Note: Application Developer may ask you if you want to switch perspectives to the Web perspective. Since our example is based on the J2EE (default) perspective, it would be best not to have it switch. Select the Remember my decision check box, and click the No button. See Figure 2.

Figure 2. Application Developer offers to switch perspectives -- select No
perspectiveno

Step 2: Create the Java class to house each web service. Expand the tree in the Project Explorer: Dynamic web Projects>Web1>Java Resources>JavaSource. Right-click on JavaSource, and select New>Class. Fill in the Package name with com.mycompany.webservices.web1 and the Name with Web1, and then click on the Finish button:

Figure 3. Creating a new Java class
newclass

Do the same for the Web2 and Web3 projects, changing the package and class names to match the project names. Note the lowercase 'w' in the package name and the upper case 'W' in the name.

Step 3: Copy in the web service code from above into each of the classes just created, and save them.

Figure 4. Web service code from Listing 1 copied into Web1.java
copycode

Be sure to change the package, class, and constructor names in the java code to match the project name. Application Developer will remind you with error markers if you forget. Here is Web2.java before the search and replace operation happened:

Figure 5. Errors automatically flagged in Application Developer
bugs

Note the lowercase 'w' in the package name, and the upper case 'W' in the class and constructor names.

You now have three very simple classes built. These will become our web services. For now, they are not particularly "J2EE-ish"; they aren't even very Bean-like. They're just simple classes -- even the lowliest Java class can become a web service.


Generating Static Clients

Now it's time to have Application Developer create the static stub classes. Right click on the Web1.java file from the Project Explorer, and make sure you see the menu item Web services>Create Web Service from the context menu. (If you don't see web services in your context menu, see the sidebar "Enabling Web service creation in Application Developer".) The Create web Service menu item actually does several things behind the scenes. Most importantly, for our purposes, it generates the static client classes into other projects so they can call a web service. So, for example, we want Web2 to be able to call Web1. That means that we need to have Web1 generate its static client classes into the Web2 project, so it can resolve it.

We want to create a complete interconnection of web services. That means that we need to have Web1 create a client into Web2 and Web3, Web2 to create a client into Web1 and Web3, and finally Web3 to create a client into Web1 and Web2. You can see that this approach would fall apart quickly for more web services. Full interconnection among web services would require n*(n-1) clients. After about four or five web services, you'd just be spending all your time generating clients. Instead, you'd want to get some form of automation involved. Since we only have three for this example, let's go ahead and do it manually.

Note: In order for the client generating step to work, your test WebSphere Application Server V6.0 within Application Developer should be started. If it isn't, you'll get this very generic error message when you are just about done:

Figure 6. Error dialog if WebSphere Application Server isn't started yet
error

If you receive this error message, just dismiss the dialog box, start the server, and redo the step you were on.

Now that we're ready to start, right click on the Web1.java file in the Project Explorer again, and select the Web services>Create Web Service menu item. On the Web Services page that comes up, ensure the Generate a proxy checkbox is selected, and choose the Next button.

Figure 7. Generating web services and proxies
generate

On the Object Selection Page that comes up, click Next.

Figure 8. Verifying the "Bean" (Web service class) location
generate2

On the Service Deployment Configuration page, pay particular attention to the Client project item in the Client-Side Environment Selection section. This is where you establish the interconnection among the web services. Since we're starting with the Web1 web service, the first client we generate will be put into the Web2 Client project; so select it from the drop-down list, and click Finish.

Figure 9. Selecting target project for generated client proxy
generate3

Application Developer may pop up a message about automatic overwriting of files now:

Figure 10. Enable automatic file overwriting
generate4

For our purposes, we want it to go ahead and do so. Dismiss the message with Yes to all. We step through this same process for each of the three web services, generating clients into each of the other two projects. Even though the menu item says 'Create Web Service,' it really means to create or to generate other artifacts related to a web service. Don't worry about running it repeatedly.

Table 1. Cross-pollinating clients
From this project:Generate into this Client project:
Web1Web2
Web1Web3
Web2Web1
Web2Web3
Web3Web1
Web3Web2

When you're done, each project should contain the following packages: com.mycompany.webservices.web1, com.mycompany.webservices.web2, and com.mycompany.webservices.web3:

Figure 11. Web1 project with access to Web2 and Web3 web services
generate5

The "local" project will only contain two Java files; the other two "remote" projects will contain six Java files.


Making the web service connection

For our little collection of web services, we'll add in the JSR 109 web service calling code, as well some rudimentary logic, to hardcode a path for them to follow. The components of such a web service call are as follows:

Listing 2. The business end of a JSR 109 web service call
InitialContext ctx;
Web1 web1 = null;
ctx = new InitialContext();
Web1Service serviceLookup1 = 
  (Web1Service) ctx.lookup("java:comp/env/service/Web1Service");
web1 = serviceLookup1.getWeb1();
result = web1.doWork(work);

We establish our context, look up, and instantiate a stub to a web service, and then call the web service's method to do our work. And this, of course, is perfectly legal to do from within a web service itself.

Web1.java, fully enabled to call its neighbors now, looks like this:

Listing 3. A web service that calls other web services
package com.mycompany.webservices.web1;

import java.rmi.RemoteException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.xml.rpc.ServiceException;
import com.mycompany.webservices.web2.*;
import com.mycompany.webservices.web3.*;

public class Web1
{
  public Web1()
  {
  }

  public String doWork(int work)
  {
    InitialContext ctx;
    Web2 web2 = null;
    Web3 web3 = null;
    String result = "";
    try
    {
      ctx = new InitialContext();
      Web2Service serviceLookup2 = 
        (Web2Service) ctx.lookup("java:comp/env/service/Web2Service");
      web2 = serviceLookup2.getWeb2();
      Web3Service serviceLookup3 = 
        (Web3Service) ctx.lookup("java:comp/env/service/Web3Service");
      web3 = serviceLookup3.getWeb3();

      if (1 == work)
      {
        result = web2.doWork(work);
        result = web3.doWork(work);
      }
    }
    catch (NamingException e)
    { e.printStackTrace(); }
    catch (ServiceException e)
    { e.printStackTrace(); }
    catch (RemoteException e)
    { e.printStackTrace(); }
    return "Web1 called with work: " + work;
  }
}

You'll notice the addition of some J2EE headers, as well as references to our static clients com.mycompany.webservices.web2* and com.mycompany.webservices.web3*. Web2.java has a few modifications in the doWork() method to just call Web3:

Listing 4. Web2's doWork() implementation
public String doWork(int work)
{
  InitialContext ctx;
  Web1 web1 = null;
  Web3 web3 = null;
  String result = "";
  if (1 == work)
  {
    try
    {
      ctx = new InitialContext();
      Web3Service serviceLookup3 = 
        (Web3Service) ctx.lookup("java:comp/env/service/Web3Service");
      web3 = serviceLookup3.getWeb3();
      result = web3.doWork(work);
    }
    catch (NamingException e)
    { e.printStackTrace(); }
    catch (ServiceException e)
    { e.printStackTrace(); }
    catch (RemoteException e)
    { e.printStackTrace(); }
  }
  return "Web2 called with work: " + work;
}

Web3.java's doWork() method will call back to Web2, depending on the parameter passed to it. Further, it changes the parameter it passes when calling Web2 (to keep us from calling in an endless loop).

Listing 5. Web3's doWork() implementation
public String doWork(int work)
{
  InitialContext ctx;
  Web2 web2 = null;
  String result = "";
  try
  {
    ctx = new InitialContext();
    Web2Service serviceLookup2 = 
      (Web2Service) ctx.lookup("java:comp/env/service/Web2Service");
    web2 = serviceLookup2.getWeb2();
    if (1 == work)
    {
      result = web2.doWork(3);
    }
  }
  catch (NamingException e)
  { e.printStackTrace(); }
  catch (ServiceException e)
  { e.printStackTrace(); }
  catch (RemoteException e)
  { e.printStackTrace(); }
  return "Web3 called with work: " + work;
}

Taking the web services out for a test drive

Once we have our web services built within Application Developer, they are automatically deployed on the test server and are ready to invoke. An easy way to test your web services is right from the Project Explorer. Expand Web Services>Services:

Figure 12. Selecting Web1Service to invoke
testservices

Right-click on Web1Service, and select the Test with Web Services Explorer menu item. This brings up the web services explorer. We can prepare to invoke Web1's doWork() method by clicking on it in the explorer. Fill in the number "1" for the work variable, and hit the Go button. They're off!

Figure 13. Web Services Explorer ready to invoke Web1's doWork method
invoker

You can see the result of calling Web1's doWork() method:

doWorkReturn (string):  Web1 called with work: 1

The activity of the rest of our web services is something of a mystery, except for trace statements to WebSphere Application Server's System.out log file. We'll conclude by briefly looking at an alphaWorks technology to remedy that situation.


Visualizing web services with IBM Web Services Navigator

The alphaWorks technology, IBM Web Services Navigator and its companion Data Collector for IBM Web Services Navigator, work together to gather and display web service activity within the WebSphere environment. After following their installation and deployment instructions, a single invocation of Web1's doWork() method produces the following transaction flow diagram:

Figure 14. Web service transaction traces revealed
viewtrans

This traces the call sequence through the various web services: Web1 to Web2, Web2 to Web3, Web3 to Web2; then everyone returns to Web1, and so on.

The service topology view shows us a graph of callers:

Figure 15. Web service topology/call graph
topology

There are many more features available in the IBM Web Services Navigator. Be sure to read more about them at the alphaWorks web site. (See Resources.) Now that your web services are talking to each other, they've got something to show you.


Conclusion

Having one web service call another isn't any more difficult than having some standalone Java code do the job. But there are some things you will have to do within Application Developer to keep the environment apprised of what you're up to. You need to enable "communications" among web services you will require. We stepped through the creation and communication of three cooperating web services in RAD6. Finally, visualizing their communications using the IBM alphaWorks Web Services Navigator opened up a whole new dimension of previously hidden information.


Download

DescriptionNameSize
Project interchange filews-coopradcode.zip246 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into SOA and web services on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=58827
ArticleTitle=Create cooperating web services with Rational Application Developer V6
publish-date=04082005