|
Development Guide |
This chapter walks you through the basic steps and typical scenarios for using the Web Service Wizard to generate Web Services from a variety of sources. Topics include:
To learn about the steps and scenarios for using the wizard when you want a program to access Web Services, see
Generating Web Service Consumers.
You can use the Web Service Wizard of Workbench to develop standard (SOAP-based) Web Services that are implemented as Java remote objects (using RMI). The wizard generates Java source files based on JAX-RPC (Java API for XML-based RPC) and jBroker Web (the JAX-RPC implementation included with Novell exteNd). JAX-RPC is the J2EE specification that provides Web Service support.
The generated files include a servlet to handle access to your Web Service and its methods from HTTP SOAP requests. You can use the generated files as is or modify them when necessary. The advantage of this Java-oriented approach is that you can deal with Web Services using the familiar technologies of RMI and J2EE instead of coding lower-level SOAP APIs.
For an introduction to Web Service concepts, standards, and technologies, see
Understanding Web Services.
For detailed documentation on the wizard, see the Web Service Wizard chapter in the Tools Guide.
The complete development process involves:
To prepare for using the Web Service Wizard, you:
Set up a WAR project in Workbench.
For each Web Service you generate, the wizard creates a servlet to handle access to that Web Service (from HTTP SOAP requests). As a result, a WAR is required to package your Web Services (one or more per WAR) for deployment to a J2EE server where they will run.
A possible variation is to set up a JAR subproject in your WAR and use that JAR to contain the servlet and other classes for a Web Service. In any case, the servlet mapping will be in the WAR's deployment descriptor (web.xml).
(Note that the approach of using a JAR subproject is not currently supported by the Web Service Wizard when you generate a Web Service from a WSDL file. In this situation, it only supports a WAR project.)
Add these files to the project:
|
Files |
Details |
|---|---|
|
Source files, classes, or archives from which your Web Services are to be generated |
You can generate a Web Service from any one of the following: No matter which one you provide, it should (at minimum) declare the methods you want your generated Web Service to expose. Compile your Java files If you provide any Java files, make sure you compile them in your project before starting the Web Service Wizard (because the wizard works from compiled classes). Edit your WSDL bindings If you provide any WSDL files, edit them as needed to make sure the SOAP address in the service definition specifies the correct binding URL. The Web Service Wizard will use this URL in the files it generates for your Web Service. |
|
Archives required by jBroker Web: |
You'll find these JARs in the Workbench compilelib directory. Depending on your J2EE server configuration, you should do one of the following:
|
Edit the classpath of your project so you can compile your Web Service classes once they're generated and edited. You'll need to include:
If you use SOAP message handlers (an advanced JAX-RPC feature) in your application, the project will also require the following archives: activation.jar, commons-logging.jar, dom4j.jar, jaxp-api.jar, and saaj-ri.jar. You'll find these JARs in the Workbench compilelib directory.
Once you've set up your WAR project, you're ready to use the Web Service Wizard. The wizard produces one Web Service at a time, so you'll need to use it multiple times if you have several to develop.
Each time you launch the wizard, it takes input from you about the kind of Web Service to produce. It then generates a set of source files that together make up the Web Service. Here's a summary of the process:
Select File>New to display the New File dialog and go to the Web Services tab.
Launch the Web Service Wizard by doing one of the following:
When the wizard prompts you for project location information, specify:
The WAR or JAR project you set up to contain the generated Web Service files (if you're generating from a WSDL file, the wizard currently requires you to specify a WAR project here)
The target directory and package in that project (if you're generating from a Java class, you won't have to fill in some of these settings; the wizard will automatically handle them for you)
If you specify a JAR project to contain the generated Web Service files, the wizard will also ask you for a WAR project to map the Web Service's servlet.
When the wizard prompts you, select the class or WSDL file to generate the Web Service from.
The wizard then asks for additional information based on your selection:
When the wizard prompts you for class-generation and SOAP options, you need to choose and configure the set of source files to generate for your Web Service.
The most important choice is whether to generate skeletons to be tie-based or not. The answer depends on the architectural model you want the implementation of your Web Service to follow. See Choosing an implementation model.
You can choose to generate stubs (which come with a simple client application) for testing your Web Service. When generating from a Java class, you can also request a WSDL file (for publishing the Web Service to a registry) as well as specify the binding style (document or RPC) and service address (URL) for the Web Service. When generating from a WSDL file, you can specify how complex types are to be mapped.
NOTE: Support for jBroker Web 1.x applications is available via a backward-compatibility option. For more information, see If you choose jBroker Web 1.x compatibility.
Click Finish when you're done specifying options for the Web Service.
When you finish the wizard, it generates everything you've specified for your Web Service and updates other parts of your project with supporting changes:
When generating file names, the Web Service Wizard follows the naming rules specified by JAX-RPC. If you start with a Java class, the resulting file names are based on the name of that class. If you start with WSDL, the resulting file names are based on the definitions in that WSDL.
For simplicity, this documentation uses xxx to represent the portion of a generated Web Service file name that's derived from a class name or WSDL definition.
Under the covers, the Web Service Wizard uses the jBroker Web compilers when generating the Web Service files listed above. In some cases, these compilers may generate additional code or files to support requirements specific to your application, such as:
For more information, see the jBroker Web help.
The current version of jBroker Web provides a high degree of backward compatibility with earlier versions. However, some changes introduced to support the JAX-RPC standard may require you to modify code when upgrading an application that originated in jBroker Web 1.x. These changes involve the conventions used for:
File names JAX-RPC specifies rules for naming certain Web Service files. In order to follow these rules while keeping all generated names simple and consistent, new name patterns were adopted (for details, see Generated 1.x-compatible files below).
Stub access in client code With JAX-RPC, clients use a Service object to instantiate the stub instead of looking up the stub directly via JNDI.
Although it's recommended that you upgrade to the current jBroker Web and JAX-RPC conventions, it's not required. By using the jBroker Web 1.x compatibility option in the Web Service Wizard, you can generate Web Service files according to the original jBroker Web conventions for file names and stub access. This enables you to take advantage of all the other improvements in the latest version of jBroker Web without altering your existing 1.x applications.
Generated 1.x-compatible files The following table describes the files generated when you use the jBroker Web 1.x compatibility option:
Follow these guidelines when editing the files generated by the Web Service Wizard:
|
Guideline |
Details |
|---|---|
|
File you may need to edit |
|
|
File you must edit |
|
|
Files you should not edit |
It's OK to edit any of the other generated files, but not typically required.
In some cases, completing the implementation of your Web Service may require you to add one or more manually coded files to work with the generated ones. See Creating additional files.
The generated xxxTie.java file includes a couple of methods you may need to edit.
If you start with a JavaBean or Java class, init() is generated to call the setTarget() method of xxx_ServiceTieSkeleton and pass an instance of xxxDelegate (to delegate to it). If xxxDelegate provides an empty constructor, the generated code uses that constructor to do the instantiation.
But if no implicit or explicit empty constructor is available, you must modify the code to indicate which one to use. You may also want to modify it to use a constructor that expects an argument.
The wizard automatically generates calls to setTarget() for every public constructor of xxxDelegate. Each line is commented outexcept the one that uses the empty constructor (if available). Uncomment the line with the constructor you want and make any related changes:
//super.setTarget( new MyObjectWSDelegate( java.lang.String arg0) );
//super.setTarget( new MyObjectWSDelegate( java.lang.String arg0, java.lang.String arg1) );
super.setTarget( new MyObjectWSDelegate( ) );
If you start with a Java remote interface or WSDL file, init() is always generated with the setTarget() call commented out. In this case, you must provide a class of your own to instantiate and delegate to:
//super.setTarget(new CONSTRUCT_YOUR_SERVICE_OBJECT_HERE);
If you start with an EJB session bean, you shouldn't need to edit the generated init() method.
This method is generated to handle HTTP GET requests sent to your Web Service. It returns the WSDL file for the Web Service, if available. Otherwise, it notifies the user that GET requests are not supported.
If you want to implement your own HTTP GET behavior, you can customize the doGet() code. If you want to use the default SOAP behavior, you can remove this code or comment it out.
Before you can test your Web Service with xxxClient, you must edit the generated xxxClient.java file to call one or more methods of the Web Service. Look for the process() method in this file and you'll find comments listing all of the possible method calls:
// System.out.println("Test Result = " + remote.getString());
// System.out.println("Test Result = " + remote.setString(java.lang.String));
// System.out.println("Test Result = " + remote.sayHello());
Uncomment the method call(s) you want to test and supply appropriate argument values, as needed:
// System.out.println("Test Result = " + remote.getString());
System.out.println("Test Result = " + remote.setString(args[0]));
System.out.println("Test Result = " + remote.sayHello());
For additional changes you may want to make to the generated xxxClient.java file, see
Generating Web Service Consumers.
In many scenarios, once the wizard finishes generating, you'll have all of the Java source files you need for your Web Service. But there are cases where you must code additional classes yourself:
To use the Web Service files generated by the wizard, you:
Update the deployment descriptor, if necessary.
When you use the tie model, the wizard automatically updates the WAR project's web.xml file with the appropriate servlet mapping for your Web Service. But with the skeleton model, you must edit web.xml yourself to supply this information.
In the following example, MyService is the servlet class that the developer has coded for the Web Service MyRemote:
<servlet> <servlet-name>MyService</servlet-name> <servlet-class>com.exsamp.rem.MyService</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyService</servlet-name> <url-pattern>MyRemote</url-pattern> </servlet-mapping>
Update the project, if necessary.
As the wizard works, it automatically adds files to your project classpath and contents, as needed. But you should also check yourself to make sure the project has everything it requires to compile and run.
For instance, if your Web Service accesses an EJB session bean, the EJB-client JAR file should be on your project's classpath.
For details on setting up the required classpath and contents for your project (including what jBroker Web needs), see
Preparing to generate.
Build and archive the project.
When you complete this step, you'll have a WAR file containing the Web Service(s) you've generated.
Set up for deployment to your J2EE server.
Prepare the server-specific deployment information required to deploy the WAR to your J2EE server. For example, if you're going to deploy to the Novell exteNd Application Server, create an exteNd deployment plan file.
If you're going to deploy from Workbench, you should also set up a server profile for your J2EE server.
Deploy the WAR to your J2EE server.
When you complete this step, each Web Service in the WAR will be accessible as a servlet that can respond to standard HTTP SOAP requests for your exposed methods.
Test your Web Service(s) running on the J2EE server.
If you've generated, edited, and compiled the xxxClient class for a Web Service, you can use it for a quick test of your method calls. To run xxxClient from Workbench, select Project>Run Web Service Client Class. The Web Service Wizard Client Runner displays, offering you a list of client classes from the current project to choose from.
You can also run xxxClient from a command line (providing that you include the appropriate directories and archives on your system classpath).
For further details on running xxxClient, see
Generating Web Service Consumers.
There are two basic implementation models you can choose from when developing with the Web Service Wizard. This section explores these choices to help you select the one that's most appropriate for the Web Services you generate:
Here's an overview of the tie model and when to use it:
It's possible (but not as common) to use the tie model when you have only a Java remote interface or WSDL file to provide as input to the Web Service Wizard. In this case, the wizard output leaves the delegation part of the model for you to complete later. You'll then need to code an implementation class and edit the generated tie class to instantiate it and delegate to it.
Here's an overview of the skeleton model and when to use it:
In this scenario, you'll see how the Web Service Wizard can be used to generate a Web Service based on an existing Java class that implements the methods to expose:
Implementation model This scenario illustrates use of the tie model. For an overview of that architecture, see Choosing an implementation model.
The WAR project for this scenario is set up as follows:
WebServiceSample.spf
The archive resulting from this project will be:
WebServiceSample.war
The initial content of this project is:
WEB-INF lib jbroker-web.jar jaxrpc-api.jar saaj-api.jar xerces.jar classes com exsamp obj MyObject.java web.xml
The classpath needed for this project is:
...\WEB-INF\lib\jbroker-web.jar
...\WEB-INF\lib\jaxrpc-api.jar
...\WEB-INF\lib\saaj-api.jar
...\WEB-INF\lib\xerces.jar
...\exteNdWorkbench\compilelib\j2ee_api_1_n.jar
Here's the input provided to the Web Service Wizard for this scenario:
MyObject is an existing Java class from which the Web Service is to be generated. It implements the methods to expose. MyObject.java contains the following code (which must be compiled before you start the wizard):
package com.exsamp.obj;
public class MyObject {
private String s;
public MyObject() {
}
public MyObject(String xxx) {
}
public MyObject(String xxx, String yyy) {
}
public String getString() {
return s;
}
public boolean setString(String s) {
this.s = s;
return true;
}
public String sayHello() {
return "Hello there, I am on the server";
}
}
This wizard panel is completed as follows:
This wizard panel is completed as follows:
This wizard panel is completed as follows:
This wizard panel is completed as follows:
Based on the input provided for this scenario, the Web Service Wizard generates these files to implement the Web Service:
MyObjectWS is the remote interface for the Web Service. The wizard generates this source code for it:
package com.exsamp.obj;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MyObjectWS extends Remote
{
public java.lang.String getString( )
throws RemoteException;
public boolean setString( java.lang.String arg0 )
throws RemoteException;
public java.lang.String sayHello( )
throws RemoteException;
}
MyObjectWS_ServiceSkeleton is the abstract servlet class that handles access to the Web Service. The wizard generates this source code for it:
package com.exsamp.obj;
import java.rmi.RemoteException;
import java.util.Properties;
import com.sssw.jbroker.web.encoding.TypeMappingRegistry;
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
public abstract class MyObjectWS_ServiceSkeleton
extends com.sssw.jbroker.web.portable.ServletSkeleton
implements MyObjectWS
{
private static final com.sssw.jbroker.web.QName _portType =
new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "MyObjectWS");
public MyObjectWS_ServiceSkeleton()
{
super(_portType);
_setProperty("xmlrpc.schema.uri", "http://www.w3.org/2001/XMLSchema");
_setProperty("version", "1.1");
}
private static java.util.Dictionary _atable = new java.util.Hashtable();
static {
_atable.put("\"urn:com.exsamp.obj.MyObject/setString\"", new java.lang.Integer(0));
_atable.put("\"urn:com.exsamp.obj.MyObject/getString\"", new java.lang.Integer(1));
_atable.put("\"urn:com.exsamp.obj.MyObject/sayHello\"", new java.lang.Integer(2));
}
private static java.util.Dictionary _mtable = new java.util.Hashtable();
static {
_mtable.put("setString", new java.lang.Integer(0));
_mtable.put("getString", new java.lang.Integer(1));
_mtable.put("sayHello", new java.lang.Integer(2));
}
public com.sssw.jbroker.web.portable.ServerResponse
_invoke(com.sssw.jbroker.web.portable.ServerRequest in) throws java.io.IOException
{
com.sssw.jbroker.web.portable.ServerResponse out = null;
String soapEncURI = "soap";
String literalURI = "literal";
try {
java.lang.Integer _m = null;
String sac = in.getAction();
if (sac != null) _m = (java.lang.Integer) _atable.get(sac);
if (_m == null) {
sac = "\"" + sac + "\"";
_m = (java.lang.Integer) _atable.get(sac);
}
if (_m == null) {
String methodName = in.getMethod();
if (methodName != null) _m = (java.lang.Integer) _mtable.get(methodName);
}
if (_m == null) throw new
com.sssw.jbroker.web.ServiceException("unable to dispatch SOAP request");
switch(_m.intValue()) {
// setString
case 0: {
in.setEncodingStyleURI(soapEncURI);
java.lang.String _arg0 = null;
try {
_arg0 = (java.lang.String)
in.readObject(java.lang.String.class, "arg0");
} catch (java.io.EOFException eofExc) {
_arg0 = null;
}
boolean result = setString(_arg0);
//create reply
out = in.createReply();
//set the content type
java.lang.Object arg = null;
arg = new java.lang.Boolean(result);
out.writeObject(arg, "result");
break;
}
// getString
case 1: {
in.setEncodingStyleURI(soapEncURI);
java.lang.String result = getString();
//create reply
out = in.createReply();
//set the content type
java.lang.Object arg = null;
arg = result;
out.writeObject(arg, "result");
break;
}
// sayHello
case 2: {
in.setEncodingStyleURI(soapEncURI);
java.lang.String result = sayHello();
//create reply
out = in.createReply();
//set the content type
java.lang.Object arg = null;
arg = result;
out.writeObject(arg, "result");
break;
}
}
} catch (java.lang.Throwable ex) {
if (System.getProperty("SOAP_DEBUG") != null) ex.printStackTrace();
out = in.createExceptionReply();
out.writeException(ex, "exception");
}
return out;
}
public boolean isDocument(String action)
{
return false;
}
private static Properties _rootHeaders = new Properties();
static {
_rootHeaders.setProperty("content-type", "text/xml; charset=UTF-8");
_rootHeaders.setProperty("content-id", "<soapbody>");
}
}
MyObjectWS_ServiceTieSkeleton is an abstract class that extends MyObjectWS_ServiceSkeleton to support the tie model. The wizard generates this source code for it:
package com.exsamp.obj;
import java.rmi.RemoteException;
import java.util.Properties;
import com.sssw.jbroker.web.encoding.TypeMappingRegistry;
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
public abstract class MyObjectWS_ServiceTieSkeleton
extends com.exsamp.obj.MyObjectWS_ServiceSkeleton
implements com.sssw.jbroker.web.portable.TieSkeleton
{
private MyObjectWS _target;
public void setTarget(java.rmi.Remote target)
{
_target = (MyObjectWS) target;
}
public java.rmi.Remote getTarget()
{
return _target;
}
public boolean setString(java.lang.String _arg0)
throws java.rmi.RemoteException
{
return _target.setString(_arg0);
}
public java.lang.String getString()
throws java.rmi.RemoteException
{
return _target.getString();
}
public java.lang.String sayHello()
throws java.rmi.RemoteException
{
return _target.sayHello();
}
}
MyObjectWSTie extends the abstract servlet classes to function as the front end for the Web Service. To process requests (method calls) it receives, this servlet instantiates and delegates to MyObjectWSDelegate. The wizard generates this source code for it:
package com.exsamp.obj;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyObjectWSTie extends MyObjectWS_ServiceTieSkeleton
{
public void init() throws ServletException
{
try
{
super.init();
// The following are all public constructors for the implemented service
// class. IMPORTANT NOTE: If available, the empty constructor has been
// implemented by default. If no implicit or explicit empty constructor
// is available, you *must* select one from the list below and uncomment
// it in order to construct the generated service implementation.
//super.setTarget( new MyObjectWSDelegate( java.lang.String arg0) );
//super.setTarget( new MyObjectWSDelegate( java.lang.String arg0, java.lang.String arg1) );
super.setTarget( new MyObjectWSDelegate( ) );
}
catch (Exception _e)
{
throw new ServletException(_e);
}
}
// The following method may be freely modified to provide custom behavior
// when an HTTP GET request is made. Comment-out or remove this method to
// provide default SOAP doGet functionality.
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
try
{
StringBuffer sb = new StringBuffer(1024);
OutputStream out = null;
InputStream in = null;
String path = "/MyObjectWS.wsdl";
try
{
// Try to load the WSDL file.
in = getServletConfig().getServletContext().getResourceAsStream(path);
if (in == null)
{
// If it can't be found, return a default message.
sendDefaultMsg(response);
}
else
{
// Try to determine the WSDL file's character encoding for content-type.
byte[] buf = new byte[512];
int read = in.read(buf);
if (read <= 0)
sendDefaultMsg(response);
String cs = getXMLEncoding(buf);
StringBuffer ct = new StringBuffer(64);
ct.append("text/xml");
if (cs != null)
{
ct.append("; charset=");
ct.append(cs);
}
// Return the WSDL file.
response.setContentType(ct.toString());
out = response.getOutputStream();
do
{
out.write(buf, 0, read);
} while ((read = in.read(buf)) >= 0);
}
}
catch (Exception _e)
{
throw new ServletException("Exception trying to return " + path, _e);
}
finally
{
if (out != null)
out.close();
if (in != null)
in.close();
}
}
catch (Exception _e)
{
throw new ServletException(_e);
}
}
// Try to determine the character encoding of this XML document.
public static String getXMLEncoding(byte[] bytes)
{
String lsLine = "";
String lsEncoding = "UTF-8";
if (bytes.length >=2 && bytes[0]==0xFE && bytes[1]==0xFF)
return "UTF-16";
String lsState = "";
int liDeclStart = 0;
int liDeclLength = 0;
for (int i=0; i < bytes.length; i++)
{
if (lsState.equals("") && bytes[i] == '<' && bytes[i+1] == '?')
{
lsState = "<?";
}
else
{
if (lsState.equals("<?") && bytes[i] == 'x' && bytes[i+1] == 'm'
&& bytes[i+2] == 'l' && bytes[i+3] == ' ')
{
liDeclStart = i;
lsState = "xml";
}
else
{
if (lsState.equals("xml") && bytes[i] == '?' && bytes[i+1] == '>')
{
liDeclLength = i - liDeclStart;
break;
}
}
}
}
lsLine = new String(bytes, liDeclStart, liDeclLength);
int liPos = lsLine.indexOf("encoding");
if (liPos > 0)
{
lsLine = lsLine.substring(liPos + 8);
int liEncStart = lsLine.indexOf('"');
int liEncEnd = lsLine.indexOf('"', liEncStart +1);
if (liEncStart < 0 && liEncEnd < 0)
{
liEncStart = lsLine.indexOf("'");
liEncEnd = lsLine.indexOf("'", liEncStart +1 );
}
if (liEncStart >= 0 && liEncEnd >= 0)
lsEncoding = lsLine.substring(liEncStart + 1, liEncEnd);
}
return lsEncoding;
}
static private final String DEFAULT_MESSAGE =
"<html><head><title>Novell exteNd Web Service</title>" +
"</head><body><h3 align=\"center\">Novell exteNd Web Service</h3>" +
"By default, SOAP servers do not communicate via HTTP GET requests. The Novell " +
"exteNd Web Service Wizard has generated an overloaded version of the " +
"<i>doGet()</i> method for your convience. This method, found in your " +
"generated _TIE code, is producing this message. If the WSDL file for this Web Service " +
"is available in the root of your Web Service WAR, this method will return the WSDL instead " +
"of this default message. You may add any custom code you like in your generated _TIE's " +
"<i>doGet()</i> method to handle HTTP GET support.</body></html>";
private void sendDefaultMsg(HttpServletResponse response) throws IOException
{
PrintWriter out = null;
try
{
response.setContentType("text/html");
response.setContentLength(DEFAULT_MESSAGE.length());
out = response.getWriter();
out.print(DEFAULT_MESSAGE);
}
finally
{
if (out != null) out.close();
}
}
}
MyObjectWSDelegate instantiates the implementation class (MyObject) and makes the requested method calls against that instance. The wizard generates this source code for it:
package com.exsamp.obj;
import java.rmi.Remote;
import java.rmi.RemoteException;
public class MyObjectWSDelegate implements MyObjectWS
{
private MyObject m_objMyObject;
public MyObjectWSDelegate( java.lang.String arg0 )
{
m_objMyObject = new MyObject( arg0 );
}
public MyObjectWSDelegate( java.lang.String arg0, java.lang.String arg1 )
{
m_objMyObject = new MyObject( arg0, arg1 );
}
public MyObjectWSDelegate( )
{
m_objMyObject = new MyObject( );
}
public java.lang.String getString( )
throws RemoteException
{
return m_objMyObject.getString( );
}
public boolean setString( java.lang.String arg0 )
throws RemoteException
{
return m_objMyObject.setString( arg0 );
}
public java.lang.String sayHello( )
throws RemoteException
{
return m_objMyObject.sayHello( );
}
}
This generated file describes the Web Service in standard WSDL format (useful when publishing to a registry):
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="MyObjectWSService"
targetNamespace="urn:com.exsamp.obj.MyObject"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="urn:com.exsamp.obj.MyObject"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<types/>
<message name="setStringRequest">
<part name="arg0" type="xsd:string"/>
</message>
<message name="setStringResponse">
<part name="result" type="xsd:boolean"/>
</message>
<message name="getStringRequest"/>
<message name="getStringResponse">
<part name="result" type="xsd:string"/>
</message>
<message name="sayHelloRequest"/>
<message name="sayHelloResponse">
<part name="result" type="xsd:string"/>
</message>
<portType name="MyObjectWS">
<operation name="setString" parameterOrder="arg0">
<input message="tns:setStringRequest"/>
<output message="tns:setStringResponse"/>
</operation>
<operation name="getString">
<input message="tns:getStringRequest"/>
<output message="tns:getStringResponse"/>
</operation>
<operation name="sayHello">
<input message="tns:sayHelloRequest"/>
<output message="tns:sayHelloResponse"/>
</operation>
</portType>
<binding name="MyObjectWSBinding" type="tns:MyObjectWS">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="setString">
<soap:operation soapAction="urn:com.exsamp.obj.MyObject/setString"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:com.exsamp.obj.MyObject" use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:com.exsamp.obj.MyObject" use="encoded"/>
</output>
</operation>
<operation name="getString">
<soap:operation soapAction="urn:com.exsamp.obj.MyObject/getString"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:com.exsamp.obj.MyObject" use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:com.exsamp.obj.MyObject" use="encoded"/>
</output>
</operation>
<operation name="sayHello">
<soap:operation soapAction="urn:com.exsamp.obj.MyObject/sayHello"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:com.exsamp.obj.MyObject" use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:com.exsamp.obj.MyObject" use="encoded"/>
</output>
</operation>
</binding>
<service name="MyObjectWSService">
<port binding="tns:MyObjectWSBinding" name="MyObjectWSPort">
<soap:address location="http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"/>
</port>
</service>
</definitions>
Based on the input provided for this scenario, the Web Service Wizard generates these files so you can test the Web Service once it's deployed:
MyObjectWSService is the service interface that's used in JAX-RPC to help clients obtain the stub for the Web Service. The wizard generates this source code for it:
package com.exsamp.obj;
import javax.xml.rpc.ServiceException;
public interface MyObjectWSService extends javax.xml.rpc.Service
{
public MyObjectWS_Stub getMyObjectWSPort()
throws ServiceException;
}
MyObjectWSServiceImpl is the service implementation class that handles instantiation of the stub (MyObjectWS_Stub). The wizard generates this source code for it:
package com.exsamp.obj;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.Properties;
import java.util.ArrayList;
import java.net.URL;
import java.net.MalformedURLException;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceException;
import com.sssw.jbroker.web.Binding;
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
public class MyObjectWSServiceImpl
extends com.sssw.jbroker.web.xml.rpc.ServiceImpl
implements MyObjectWSService
{
public MyObjectWSServiceImpl()
{
try {
createCalls();
} catch (ServiceException ex) {
throw new javax.xml.rpc.JAXRPCException("failed to create the call objects: " + ex.getMessage());
}
}
public QName getServiceName() { return _serviceName; }
public Iterator getPorts() { return _portMapping.keySet().iterator(); }
public void setProxyMode(boolean proxy) { _proxy = proxy; }
public boolean getProxyMode() { return _proxy; }
public URL getWSDLDocumentLocation()
{
return null;
}
public java.rmi.Remote getPort(Class serviceDefInterface)
throws ServiceException
{
if (serviceDefInterface == null)
throw new ServiceException("No Service class specified.");
if (!java.rmi.Remote.class.isAssignableFrom(serviceDefInterface))
throw new ServiceException("Class is not a valid Interface.");
String stubName = (String) _intfMapping.get(serviceDefInterface);
Binding binding = (Binding) _intfBinding.get(serviceDefInterface);
if (stubName == null)
return getPort(serviceDefInterface, binding,
_classInfo, _typeMappingRegistry, null);
else
return getPort(stubName, binding, _typeMappingRegistry);
}
public java.rmi.Remote getPort(QName portName, Class serviceDefInterface)
throws ServiceException
{
return getPort(portName, serviceDefInterface, getProxyMode());
}
public java.rmi.Remote getPort(QName portName, Class serviceDefInterface, boolean proxy)
throws ServiceException
{
if (((proxy==false) || (serviceDefInterface == null)) &&
(portName != null)) {
String stubName = (String) _portMapping.get(portName);
Binding binding = (Binding) _portBinding.get(portName);
if (stubName == null) return getPort(null, serviceDefInterface);
try {
return getPort(stubName, binding, portName,
_typeMappingRegistry);
} catch (Exception ex) {
return getPort(null, serviceDefInterface);
}
} else {
if (serviceDefInterface == null)
throw new ServiceException("No Service class specified.");
if (!java.rmi.Remote.class.isAssignableFrom(serviceDefInterface))
throw new ServiceException("Class is not a valid Interface.");
Binding binding = (Binding) _intfBinding.get(serviceDefInterface);
String uri = (portName == null) ? null : portName.getNamespaceURI();
return getPort(serviceDefInterface, binding, _classInfo,
_typeMappingRegistry, uri);
}
}
public Call[] getCalls(QName portName)
throws ServiceException
{
ArrayList callslist = (ArrayList) _calls.get(portName);
if (callslist == null) return null;
Call[] calls = new Call[callslist.size()];
return (Call[]) callslist.toArray(calls);
}
private void addCall(QName portName, Call call)
{
ArrayList callslist = (ArrayList) _calls.get(portName);
if (callslist == null) {
callslist = new ArrayList();
_calls.put(portName, callslist);
}
callslist.add(call);
}
public MyObjectWS_Stub getMyObjectWSPort()
throws ServiceException
{
try {
return (MyObjectWS_Stub) getPort(new QName(
"urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), null, false);
} catch (Exception ex) {
return (MyObjectWS_Stub) getPort(com.exsamp.obj.MyObjectWS.class);
}
}
private void createCalls()
throws ServiceException
{
Call call = null;
call = createCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"),
new QName("urn:com.exsamp.obj.MyObject", "setString"));
call.addParameter("arg0", new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class, ParameterMode.IN);
call.addParameter("result", new QName("http://www.w3.org/2001/XMLSchema", "boolean"), boolean.class, ParameterMode.OUT);
call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema", "boolean"), boolean.class);
call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");
call.setProperty(Call.SOAPACTION_URI_PROPERTY, "\"urn:com.exsamp.obj.MyObject/setString\"");
call.setTargetEndpointAddress("http://localhost/WebServiceSampleDB/WebServiceSample/MyObject");
addCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), call);
call = createCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"),
new QName("urn:com.exsamp.obj.MyObject", "getString"));
call.addParameter("result", new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class, ParameterMode.OUT);
call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class);
call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");
call.setProperty(Call.SOAPACTION_URI_PROPERTY, "\"urn:com.exsamp.obj.MyObject/getString\"");
call.setTargetEndpointAddress("http://localhost/WebServiceSampleDB/WebServiceSample/MyObject");
addCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), call);
call = createCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"),
new QName("urn:com.exsamp.obj.MyObject", "sayHello"));
call.addParameter("result", new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class, ParameterMode.OUT);
call.setReturnType(new QName("http://www.w3.org/2001/XMLSchema", "string"), java.lang.String.class);
call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");
call.setProperty(Call.SOAPACTION_URI_PROPERTY, "\"urn:com.exsamp.obj.MyObject/sayHello\"");
call.setTargetEndpointAddress("http://localhost/WebServiceSampleDB/WebServiceSample/MyObject");
addCall(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"), call);
}
static boolean _proxy = true;
static final QName _serviceName;
static final Hashtable _intfMapping = new Hashtable();
static final Hashtable _intfBinding = new Hashtable();
static final Hashtable _portBinding = new Hashtable();
static final Hashtable _portMapping = new Hashtable();
static final Hashtable _classInfo = new Hashtable();
private final Hashtable _calls = new Hashtable();
static {
_serviceName = new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSService");
_intfBinding.put(MyObjectWS.class, new Binding("soap", "http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"));
_portBinding.put(new QName("urn:com.exsamp.obj.MyObject", "com.exsamp.obj.MyObjectWSPort"),
new Binding("soap", "http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"));
_intfMapping.put(MyObjectWS.class, "com.exsamp.obj.MyObjectWS_Stub");
_portMapping.put(new QName("urn:com.exsamp.obj.MyObject",
"com.exsamp.obj.MyObjectWSPort"), "com.exsamp.obj.MyObjectWS_Stub");
Hashtable _methodInfo;
Hashtable _paramInfo;
Properties _props;
_methodInfo = new Hashtable();
_paramInfo = new Hashtable();
_props = new Properties();
_props.setProperty("jbroker.web.soap.action","\"urn:com.exsamp.obj.MyObject/setString\"");
_paramInfo.put("Properties", _props);
_props = new Properties();
_props.setProperty("jbroker.web.parameter.name", "arg0");
_props.setProperty("jbroker.web.parameter.inout", "1");
_paramInfo.put("Param0", _props);
_props = new Properties();
_props.setProperty("jbroker.web.parameter.name", "result");
_props.setProperty("jbroker.web.parameter.inout", "2");
_paramInfo.put("Result", _props);
_methodInfo.put("setString", _paramInfo);
_paramInfo = new Hashtable();
_props = new Properties();
_props.setProperty("jbroker.web.soap.action","\"urn:com.exsamp.obj.MyObject/getString\"");
_paramInfo.put("Properties", _props);
_props = new Properties();
_props.setProperty("jbroker.web.parameter.name", "result");
_props.setProperty("jbroker.web.parameter.inout", "2");
_paramInfo.put("Result", _props);
_methodInfo.put("getString", _paramInfo);
_paramInfo = new Hashtable();
_props = new Properties();
_props.setProperty("jbroker.web.soap.action","\"urn:com.exsamp.obj.MyObject/sayHello\"");
_paramInfo.put("Properties", _props);
_props = new Properties();
_props.setProperty("jbroker.web.parameter.name", "result");
_props.setProperty("jbroker.web.parameter.inout", "2");
_paramInfo.put("Result", _props);
_methodInfo.put("sayHello", _paramInfo);
_classInfo.put("com.exsamp.obj.MyObjectWS", _methodInfo);
}
}
MyObjectWS_Stub is used by clients as a proxy for accessing the Web Service. This stub class implements the remote interface (MyObjectWS) to handle the logistics of each method call. The wizard generates this source code for it:
package com.exsamp.obj;
import java.util.Properties;
import com.sssw.jbroker.web.core.Constants;
import com.sssw.jbroker.web.encoding.TypeMappingRegistry;
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
public class MyObjectWS_Stub
extends com.sssw.jbroker.web.portable.Stub
implements MyObjectWS
{
private static com.sssw.jbroker.web.QName _portType =
new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "MyObjectWS");
private static final com.sssw.jbroker.web.Binding[] _bindings =
new com.sssw.jbroker.web.Binding[] {
new com.sssw.jbroker.web.Binding("soap", "http://localhost/WebServiceSampleDB/WebServiceSample/MyObject"),
};
public MyObjectWS_Stub()
{
this(null);
}
public MyObjectWS_Stub(DefaultTypeMappingRegistry tmr)
{
super(_portType, _bindings);
_setProperty("xmlrpc.schema.uri", (Object) "http://www.w3.org/2001/XMLSchema".intern());
_setProperty("version", (Object) "1.1");
TypeMappingRegistry _tm = null;
try {
if (tmr != null)
_tm = tmr;
else {
_tm = new DefaultTypeMappingRegistry();
}
_setTypeMappingRegistry(_tm);
} catch (Exception ex) {
throw new javax.xml.rpc.JAXRPCException("failed to initialize type mapping registry: " + ex.getMessage());
}
}
public boolean setString(java.lang.String _arg0)
throws java.rmi.RemoteException
{
com.sssw.jbroker.web.portable.ClientResponse in = null;
try {
// create an output stream
_getDelegate().setProperty("xmlrpc.soap.operation.name",
new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "setString"));
//create request
com.sssw.jbroker.web.portable.ClientRequest out =
_request("setString", true, "soap", false, "\"urn:com.exsamp.obj.MyObject/setString\"");
_getDelegate().setProperty("soapAction", (Object) "\"urn:com.exsamp.obj.MyObject/setString\"");
_getDelegate().setProperty(Constants.HTTP_CONTENT_TYPE, (Object) "text/xml; charset=utf-8");
out._setProperties(_getDelegate().getProperties());
Object arg = null;
// marshal the parameters
arg = _arg0;
out.writeObject(arg, "arg0");
// do the invocation
in = _invoke(out);
// unmarshal the results
// return
java.lang.Boolean retWrapper = (java.lang.Boolean)in.readObject(boolean.class, "result");
boolean ret = retWrapper.booleanValue();
return ret;
} catch (java.lang.Throwable t) {
// map to remote exception
throw com.sssw.jbroker.web.ServiceException.mapToRemote(t);
}
}
public java.lang.String getString()
throws java.rmi.RemoteException
{
com.sssw.jbroker.web.portable.ClientResponse in = null;
try {
// create an output stream
_getDelegate().setProperty("xmlrpc.soap.operation.name",
new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "getString"));
//create request
com.sssw.jbroker.web.portable.ClientRequest out =
_request("getString", true, "soap", false, "\"urn:com.exsamp.obj.MyObject/getString\"");
_getDelegate().setProperty("soapAction", (Object) "\"urn:com.exsamp.obj.MyObject/getString\"");
_getDelegate().setProperty(Constants.HTTP_CONTENT_TYPE, (Object) "text/xml; charset=utf-8");
out._setProperties(_getDelegate().getProperties());
Object arg = null;
// do the invocation
in = _invoke(out);
// unmarshal the results
// return
java.lang.String ret = null;
try {
ret = (java.lang.String)
in.readObject(java.lang.String.class, "result");
} catch (java.io.EOFException eofExc) {
ret = null;
}
return ret;
} catch (java.lang.Throwable t) {
// map to remote exception
throw com.sssw.jbroker.web.ServiceException.mapToRemote(t);
}
}
public java.lang.String sayHello()
throws java.rmi.RemoteException
{
com.sssw.jbroker.web.portable.ClientResponse in = null;
try {
// create an output stream
_getDelegate().setProperty("xmlrpc.soap.operation.name",
new com.sssw.jbroker.web.QName("urn:com.exsamp.obj.MyObject", "sayHello"));
//create request
com.sssw.jbroker.web.portable.ClientRequest out =
_request("sayHello", true, "soap", false, "\"urn:com.exsamp.obj.MyObject/sayHello\"");
_getDelegate().setProperty("soapAction", (Object) "\"urn:com.exsamp.obj.MyObject/sayHello\"");
_getDelegate().setProperty(Constants.HTTP_CONTENT_TYPE, (Object) "text/xml; charset=utf-8");
out._setProperties(_getDelegate().getProperties());
Object arg = null;
// do the invocation
in = _invoke(out);
// unmarshal the results
// return
java.lang.String ret = null;
try {
ret = (java.lang.String)
in.readObject(java.lang.String.class, "result");
} catch (java.io.EOFException eofExc) {
ret = null;
}
return ret;
} catch (java.lang.Throwable t) {
// map to remote exception
throw com.sssw.jbroker.web.ServiceException.mapToRemote(t);
}
}
private static Properties _rootHeaders = new Properties();
static {
_rootHeaders.setProperty("content-type", "text/xml; charset=UTF-8");
_rootHeaders.setProperty("content-id", "<soapbody>");
}
}
MyObjectWSClient is a simple client application that accesses the Web Service by:
The wizard generates this source code for it:
package com.exsamp.obj;
import javax.naming.*;
public class MyObjectWSClient
{
public void process(String[] args) throws Exception
{
MyObjectWS remote = getRemote(args);
// The following code has been generated for your testing convenience. In
// order to successfully test your Web Service, you must uncomment one or
// more of these lines and supply meaningful arguments where necessary.
// Once you have modified the test method(s) below, compile this class and
// execute it from a command line with your class path set appropriately.
// System.out.println("Test Result = " + remote.getString());
// System.out.println("Test Result = " + remote.setString(java.lang.String));
// System.out.println("Test Result = " + remote.sayHello());
}
public MyObjectWS getRemote(String[] args) throws Exception
{
InitialContext ctx = new InitialContext();
String lookup = "xmlrpc:soap:com.exsamp.obj.MyObjectWSService";
MyObjectWSService service = (MyObjectWSService)ctx.lookup(lookup);
MyObjectWS remote = (MyObjectWS)service.getMyObjectWSPort();
return remote;
}
public static void main(String[] args)
{
try
{
MyObjectWSClient client = new MyObjectWSClient();
client.process(args);
}
catch (Exception _e)
{
System.out.println("*** Error Executing Generated Test Client ***");
_e.printStackTrace();
}
}
}
Modifications needed The process() method of the generated MyObjectWSClient.java file must be edited to uncomment the Web Service method call to be tested. Here's the change:
// System.out.println("Test Result = " + remote.getString());
// System.out.println("Test Result = " + remote.setString(java.lang.String));
System.out.println("Test Result = " + remote.sayHello());
Because this scenario uses the tie model, the Web Service Wizard automatically updates the web.xml file to declare MyObjectWSTie as the servlet class to handle requests for the MyObject Web Service:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>MyObject</servlet-name>
<servlet-class>com.exsamp.obj.MyObjectWSTie</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyObject</servlet-name>
<url-pattern>MyObject</url-pattern>
</servlet-mapping>
</web-app>
Once this project is built and the WAR file is created and deployed to the J2EE server, the MyObject Web Service is ready for a test run. Here's the result of using the Client Runner in Workbench to execute the MyObjectWSClient application:
|
Development Guide |
Copyright © 2001, 2002, 2003 SilverStream Software, LLC, a wholly owned subsidiary of Novell, Inc. All rights reserved.