Extending the Web Service to Include Bean References


When creating an Axis Web Service, you don't have to rely on the base data types like an integer, double, or string. Axis Web Services can also expose more complicated Bean-type structures. Bean-type structures can be defined using WSDL, but a simpler way is to define the methods and beans using Java, generate a WSDL file, and then generate the Java bindings. In all frankness, this sounds like an extremely long-winded way to create a Web Service. However, there is reason behind this madness, so to speak.

Generating the WSDL File

As already mentioned, the Axis toolkit contains a WSDL-to-Java generator, whose purpose is to read a number of compiled reference Java classes that define a Web Service and generate a WSDL file. You use Java classes to define a WSDL file because it is simpler to express a Web Service using Java. It is not beyond the realm of possibilities to define a Web Service using WSDL, but it does require you to understand WSDL and how to convert Java types to XML schema types.

Using this process of separating the design of the Web Service from the implementation is a design and implementation decision and is very useful. A developer can first develop the Web Service and ensure a good design before any implementation is started. In fact, you can use another toolkit called gSoap to generate the WSDL files. The gSoap toolkit gives the developer good control over the generated WSDL files without actually coding in WSDL. The tool provided within Axis that converts Java to WSDL is useful, but it doesn't allow as much fine tuning as gSoap . Working with Web Services in a production scenario and using only the Axis provided tool requires tweaking to make the Web Service do what you want. You might therefore ask, why not code directly in WSDL? The answer is that coding in WSDL without an appropriate editor can be very tedious and error-prone . The Axis WSDL or the gSoap WSDL generator tool allows developers to focus on the design issue in a programming language that they familiar with.

We will now program a sample Web Service, a Calculator service that adds and subtracts two numbers. The numbers are stored in a Java Bean. In a specific version of the Web Service that will be defined, the result of the operation is also stored in the Java Bean. Listing 10.25 is the Java Bean.

Listing 10.25
start example
 public class Number { private long _value1; private long _value2; private long _result; public long getValue1() { return _value1; } public void setValue1( long value) { _value1 = value; } public long getValue2() { return _value2; } public void setValue2( long value) { _value2 = value; } public long getResult() { return _result; } public void setResult( long value) { _result = value; } } 
end example
 

In Listing 10.25, the class Number has three properties: value1 , value2 and result . The properties value1 and value2 are used to store the two numbers that will either be added or subtracted. The property result is used to store the value of either the addition or subtraction. Listing 10.26 is the definition of the Web Service.

Listing 10.26
start example
 public class Calculator { public long add( Number num) { } public void subtract( Number num) { return; } } 
end example
 

In Listing 10.26, the class Calculator defines a Web Service that has two methods: add and subtract . In the Axis documentation, the recommendation is to define the Web Service using interfaces, but Listing 10.26 shows it is not necessary. The method add adds the properties value1 and value2 and returns the result as a long value. The method subtract subtracts the properties value1 from value2 and stores the result in the property result.

To use the WSDL generator, you have to compile Listings 10.25 and 10.26. Then, to generate the WSDL file, the WSDL generator consumes the compiled class Calculator . The class Calculator manipulates the class Number , so the WSDL generator will trace the class dependencies and generate the necessary dependent class descriptors. Listing 10.27 shows the WSDL Schema type that is generated from the class Number .

Listing 10.27
start example
 <wsdl:types> <schema  targetNamespace="calculator.devspace.ws"  xmlns="http://www.w3.org/2001/XMLSchema">  <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>  <complexType name="Number"> <sequence>  <element name="result" type="xsd:long"/>  <element name="value1" type="xsd:long"/>  <element name="value2" type="xsd:long"/> </sequence>  </complexType> </schema>  </wsdl:types> 
end example
 

In Listing 10.27, the class Number is converted into the XML Schema complex type Number . The XML tag element represents an individual property from the Number class. Had any of the properties been a reference to another bean, then that bean would have been added to the types list. Essentially what happens is that Axis will convert the bean class definition into a standard XML Schema declaration.

However, there is a problem with the generation, because Listing 10.26 defines a class method that retrieves the input from the same object that is returned to the caller. The WSDL generator does not understand this logic and hence the generated WSDL has an error, as shown in Listing 10.28.

Listing 10.28
start example
 <wsdl:message name="subtractRequest"> <wsdl:part name="num" type="impl:Number"/>  </wsdl:message>  <wsdl:message name="addResponse"> <wsdl:part name="addReturn" type="xsd:long"/>  </wsdl:message>  <wsdl:message name="addRequest"> <wsdl:part name="num" type="impl:Number"/>  </wsdl:message>  <wsdl:message name="subtractResponse">  </wsdl:message> 
end example
 

In Listing 10.28, four messages are defined. Going back to Listing 10.26, the class Calculator exposed two methods. Remembering our method of WSDL mapping, a SOAP conversation is the exchange of two SOAP messages. Therefore, Listing 10.28 is correct in defining four messages, since two methods times two is four.

Let's focus on Listing 10.26's method add . The input parameter was a Number and the return value was a long . In Listing 10.28, the request message addRequest had one part to the message, which was the XML Schema type impl:Number . The response message addResponse has one part, which is the XML Schema type long . The method add is converted correctly.

What is incorrectly converted is the method subtract from Listing 10.26. The logic used for this method is that the response is stored in the incoming object. The input message subtractRequest described in Listing 10.28 defines the XML Schema type impl:Number , which is correct. However, what is incorrect is that the message subtractResponse has no message parts . This means that the WSDL generator did not pick up on the logic. This is logical because the WSDL generator cannot assume incoming objects will always be returned. Such an assumption could be a performance bottleneck. To solve the problem of the missing return object, add a message part to the WSDL file. This is shown in Listing 10.29.

Listing 10.29
start example
 <wsdl:message name="subtractResponse">  <wsdl:part name="num" type="impl:Number"/>  </wsdl:message> 
end example
 

In Listing 10.29, the wsdl message part that is added is identical to the message subtractRequest . This small fix might make you think that the Axis toolkit is not as useful as it could be. This is extremely far from the truth. The problem of the missing returned object is not due to the Axis toolkit, but due to the fact that the Java programming language is limited in its Web Service expressions. The Axis toolkit is much better at generating Java code from a WSDL file than generating a WSDL file from Java code. Other languages are not better because Web Services have very rich expression capabilities, like multiple return data types, that traditional programming languages cannot express. Now you'll see why we referred to the Open Source gSoap Web Service toolkit earlier. gSoap is a C and C++ Web Service toolkit and is beyond the scope of this book. However, the WSDL generator for the gSoap toolkit is a special language that is based on a superset of the C header syntax. Using this special language, you can very easily define any kind of Web Service construct, without having to manually write WSDL code.

Generating the Java Stubs

Once the WSDL file has been generated, you generate the Java class stubs using the same process as described in the Generating the WSDL file section earlier in this chapter. However, this time we'll use the server stubs as well, instead of just using the client-side files. Listing 10.30 shows the generated server stub file with implemented logic.

Listing 10.30
start example
 public class MathSoapBindingImpl implements ws.devspace.calculator.Calculator { public long add(ws.devspace.calculator.Number num) throws java.rmi.RemoteException { return num.getValue1() + num.getValue2(); } public void subtract(ws.devspace.calculator.holders.NumberHolder num) throws java.rmi.RemoteException  { num.value.setResult( num.value.getValue1() - num.value.getValue2()); } } 
end example
 

In Listing 10.30, the generated server stub is a concatenation of the Web Service name and the identifier SoapBindingImpl that generates the class name MathSoapBindingImpl . The methods add and subtract have been implemented as a simple addition and subtraction operation except the results in the subtraction are assigned to an object.

Listing 10.30 is also a round trip in Java programming terms. Listing 10.26 represents the Java class file that generates the WSDL file that generates the Java class file in Listing 10.30. Using logic, you have to functionally define the classes in Listings 10.26 and 10.30.

The classes defined in Listings 10.26 and 10.30 are not functionality identical because the method subtract uses a different data type in Listing 10.30. The reason has to do with our modification of the original WSDL file and the inability of programming languages like Java to cope without parameters.

When a parameter is passed to a method in Java, the original value of the parameter can be read and modified, but the original value will not be passed back to the caller. Consider for example Listing 10.31, which allocates an object and attempts to return the object instance on the calling stack.

Listing 10.31
start example
 public void subtract( Number num) { num = new Number(); return; } 
end example
 

In Listing 10.31, the parameter num is an input parameter that is assigned a new object instance. After the method subtract returns, the object will be automatically ready for garbage collection, because Java does not have a notion of an output parameter. The parameter num is input only on the calling stack and cannot be reassigned. However, there is a trick in that a holder class is used to return the object instance. Consider Listing 10.32, which defines the class NumberHolder .

Listing 10.32
start example
 public class NumberHolder { public Number reference; } 
end example
 

The class NumberHolder contains a public data member, which references an object of data type Number If we modify Listing 10.31, the allocator in Listing 10.33 results.

Listing 10.33
start example
 public void subtract( NumberHolder holder) { holder.num = new Number(); return; } 
end example
 

In Listing 10.33, the input parameter is now the data type NumberHolder . This time, when the class Number is allocated, it is assigned to a data member. This means that the calling stack is not modified and the allocated object instance is automatically returned.

Now, let's go back to Listing 10.31. The data type NumberHolder is in essence identical to the class NumberHolder defined in Listing 10.33. The term Holder is used to denote that the class "holds" a reference to another class, which is the identifier before the term Holder . In Listing 10.33, this means it is the class Number The idea of a "holder" class is not new and has been used in the Object Management Group (OMG) bindings for Java and the Java XML RPC bindings.

Deploying the Web Service

Deploying a generated Web Service is no different than deploying a hand-coded Web Service, as we've previously shown. The major difference is that more generated items are written to the deployment file. Listing 10.34 shows a simple deployment file generated by the WSDL-to-Java generator.

Listing 10.34
start example
 <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="Math" provider="java:RPC" style="rpc" use="encoded"> <parameter name="wsdlTargetNamespace" value="calculator.devspace.ws"/> <parameter name="wsdlServiceElement" value="CalculatorService"/> <parameter name="wsdlServicePort" value="Math"/> <parameter name="className" value="ws.devspace.calculator.MathSoapBindingSkeleton"/> <parameter name="wsdlPortType" value="Calculator"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="Session"/> <typeMapping xmlns:ns="calculator.devspace.ws" qname="ns:Number" type="java:ws.devspace.calculator.Number" serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" /> </service> </deployment>` 
end example
 

In Listing 10.34, the number of parameter XML elements have increased; there is also an additional typeMapping XML element. The added parameter XML elements all relate to the WSDL mapping when SOAP messages are received and sent. The XML element typeMapping is used to automatically serialize and deserialize the bean using the standard Axis serializers. Consider this as an Axis betwixt -type mapping.

The deployment file in Listing 10.34 does not contain any configuration information regarding method names or data type mappings. This is because the class referenced by the parameter class, ws.devspace.calculator.MathSoapBindingSkeleton is a generated delegator class that provides the mapping information. Listing 10.35 shows the static constructor of the class MathSoapBindingSkeleton that maps the methods.

Listing 10.35
start example
 static { org.apache.axis.description.OperationDesc _oper; org.apache.axis.description.FaultDesc _fault; org.apache.axis.description.ParameterDesc [] _params; _params = new org.apache.axis.description.ParameterDesc [] { new org.apache.axis.description.ParameterDesc( new javax.xml.namespace.QName("", "num"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("calculator.devspace.ws", "Number"), ws.devspace.calculator.Number.class, false, false), }; _oper = new org.apache.axis.description.OperationDesc("add", _params,  new javax.xml.namespace.QName("", "addReturn")); _oper.setReturnType(  new javax.xml.namespace.QName(  "http://www.w3.org/2001/XMLSchema", "long")); _oper.setElementQName(  new javax.xml.namespace.QName("calculator.devspace.ws", "add")); _oper.setSoapAction(""); _myOperationsList.add(_oper); if (_myOperations.get("add") == null) { _myOperations.put("add", new java.util.ArrayList()); } ((java.util.List)_myOperations.get("add")).add(_oper); _params = new org.apache.axis.description.ParameterDesc [] { new org.apache.axis.description.ParameterDesc( new javax.xml.namespace.QName("", "num"), org.apache.axis.description.ParameterDesc.INOUT, new javax.xml.namespace.QName("calculator.devspace.ws", "Number"), ws.devspace.calculator.Number.class, false, false), }; _oper = new org.apache.axis.description.OperationDesc("subtract", _params, null); _oper.setElementQName(  new javax.xml.namespace.QName("calculator.devspace.ws", "subtract")); _oper.setSoapAction(""); _myOperationsList.add(_oper); if (_myOperations.get("subtract") == null) { _myOperations.put("subtract", new java.util.ArrayList()); } ((java.util.List)_myOperations.get("subtract")).add(_oper); } 
end example
 

In Listing 10.35, the methods used look extremely similar to those used in Listing 10.16, where the Web Service parameters had to be manually coded. The reason for this is that in this case the generated code uses the exact same way of defining parameters and methods as the manual code. The difference is that Listing 10.35 is automatically generated based on the WSDL file.

However, there is a problem. A bit further in the MathSoapBindingSkeleton, you typically find an instantiation to the MathSoapBindingImpl class like that shown in Listing 10.36.

Listing 10.36
start example
 public MathSoapBindingSkeleton() { this.impl = new ws.devspace.calculator.MathSoapBindingImpl(); } 
end example
 

The problem with Listing 10.36 is that a hard-coded MathSoapBindingImpl object allocation has been defined. Listing 10.31 added code to the generated file MathSoapBindingImpl . This is a problem because it means that when a WSDL change requires a new generation of the sources, one of two things can happen. Either the WSDL-to-Java generator can overwrite the already existing MathSoapBindingImpl class and force a deletion of all modifications, or the WSDL-to-Java generator knows not to overwrite and generates new class files for all of the other generated files. While the second solution seems better, the problem is that the developer will have to manually add or modify the methods in the MathSoapBindingImpl class. Either case requires tweaking of the class files. A better solution would be to remap the class reference in Listing 10.36 to a custom Calculator interface implementation instance. The problem of the WSDL-to-Java generation wiping out the changes still exists, but at least the modification is a single line of code and nothing is lost in the update. The advantage of this approach is that it entirely separates the Axis-generated files and custom-coded files. This also allows you to reuse the custom-coded files in other contexts.

The WSDL-to-Java generator can also generate a different stub binding. In this modified stub binding, the details of the binding are stored in the deployment file, as shown in Listing 10.37.

Listing 10.37
start example
 <service name="Math" provider="java:RPC" style="rpc" use="encoded"> <parameter name="wsdlTargetNamespace" value="calculator.devspace.ws"/> <parameter name="wsdlServiceElement" value="CalculatorService"/> <parameter name="wsdlServicePort" value="Math"/> <parameter name="className"  value="ws.devspace.calculator.MathSoapBindingImpl"/> <parameter name="wsdlPortType" value="Calculator"/> <operation name="add" qname="operNS:add"  xmlns:operNS="calculator.devspace.ws" returnQName="addReturn"  returnType="rtns:long" xmlns:rtns="http://www.w3.org/2001/XMLSchema" > <parameter name="num" type="tns:Number" xmlns:tns="calculator.devspace.ws"/> </operation> <operation name="subtract" qname="operNS:subtract"  xmlns:operNS="calculator.devspace.ws" > <parameter name="num" type="tns:Number"  xmlns:tns="calculator.devspace.ws" mode="INOUT"/> </operation> <parameter name="allowedMethods" value="add subtract"/> <parameter name="scope" value="Session"/> <typeMapping xmlns:ns="calculator.devspace.ws" qname="ns:Number" type="java:ws.devspace.calculator.Number" serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" /> </service> 
end example
 

In Listing 10.37, the additional XML elements operation define a method and its associated parameters. The child XML element parameter identifies a parameter for the method. The definitions created by the XML elements operation replace the declarations created in Listing 10.35. In the default mode of the WSDL-to-Java generator, the definitions of operations and parameters are stored in the deployment file.

The deployment file of Listing 10.37 has a parameter name className that references the class ws.devspace.calculator.MathSoapBindingImpl . The other deployment file defined in Listing 10.34, in contrast, references a delegator that references the class MathSoapBindingImpl . When the deployment file generates the operation and parameter description information, the class MathSoapBindingSkeleton is not generated. This then raises the problem of adding implementation code to a generated piece of code. The solution is to change the deployment parameter classname to the custom interface implementation.

Regardless of the generated deployment file's form, the registration of the Web Service is carried out the same way and uses the AdminClient tool.

Applying Scope to a Web Service Request

Making a Web Service request can involve a stateless call like HTTP. This means a request is made, some data is processed , and a response is received. If another request were made, then the Web Service would by default not realize that the same client is making a second call. This results in the inability to manage the state on the server. To get around this problem, Axis uses the concept of scope. A scope is a way of defining how objects and states will be remembered . Axis uses the following three scopes defined:

Note

Note that the term "Web Service object" refers to the parameter name className in the deployment file, which in Listing 10.37 is ws.devspace.calculator.MathSoapBindingImpl .

  • Request : In this scope, every request causes a new Web Service object to be instantiated . This type of scope is identical to the HTTP stateless protocol, where no state is saved on the server side.

  • Session : In this scope, every request from the same instantiated client-side object that is returned from the locator object is sent to the same object. If the locator object instantiates two different client-side object instances, then there will be two different Web Service object conversations.

  • Application : In this scope, the Web Service object is instantiated only once and all requests , regardless of from which client, communicate to that single instantiated object. This scope could also be considered as a singleton.

The scope of the object is defined in the deployment file, which is a result of the WSDL-to-Java generator. Referring back to Listing 10.37, the parameter name scope has a value of Session . Using the Session or Application scope enables a developer to create private data members that could represent a database connection or running total type data.

Configuration Data

If you use the information you've learned from previous chapters, it would be easy to construct a singleton object using a generic factory or a discovery mechanism. The singleton would contain the configuration information necessary for any further configuration. However, there is a way of getting configuration information from the deployment file. Listing 10.38 shows a modified version of the deployment file that contains a configuration item (note that the actual deployment file has been abbreviated for clarity).

Listing 10.38
start example
 <service name="Echo" provider="java:RPC"> <parameter name="myProperty" value="Here is what you said "/> </service> 
end example
 

In Listing 10.38, a custom parameter name myProperty has been defined. This custom parameter has a notation like the rest of the parameters. The difference is that the custom parameter has a user -defined identifier. To be able to read this parameter or any other parameter, use Listing 10.39.

Listing 10.39
start example
 public class Echo { public String doEcho( String input) { MessageContext msgCtxt = MessageContext.getCurrentContext(); return msgCtxt.getStrProp( "myProperty") + input; } } 
end example
 

In Listing 10.39, the class method Echo.doEcho has been extended to read the initial buffer string using the configuration file. The class method MessageContext.getCurrentContext is used to retrieve the current context object, which is the data type MessageContext . The class MessageContext references the current SOAP message in its raw form and other infrastructure bits. To retrieve the property value, the method getStrProp is used.




Applied Software Engineering Using Apache Jakarta Commons
Applied Software Engineering Using Apache Jakarta Commons (Charles River Media Computer Engineering)
ISBN: 1584502460
EAN: 2147483647
Year: 2002
Pages: 109

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net