Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 4
Let's define an interface for our HelloWorld service. The final version of this service in Part 2 had two overloaded versions of the method sayHelloTo(): one version took a Java string, and the other took a Name JavaBean. This is what one possible interface, called Hello, looks like:
package hello;
public interface Hello
{
public String sayHelloTo(String name);
public String sayHelloTo(Name name);
}
The service developer decides how many interfaces to create -- and what to call them. For example, you could create two interfaces
for the HelloWorld service, with each interface containing one method. In general, you should avoid creating interfaces with
more than seven methods. Also consider lumping together only those methods that make sense together. For example, if the HelloWorld
service also had a sayByeTo() method that returned a customized good-bye message to the caller, it would make sense to have two separate interfaces: one
for the sayHelloTo() methods and one for the sayByeTo() methods.
Now that we have the interface that defines the contract between the HelloWorld service and the client, let's return our attention
to the newInstance() method. As I mentioned above, the newInstance() method creates a new instance of the Proxy class. The method can do that because it is part of Proxy and can access the private constructor. newInstance() then calls the initialize() method on the newly created instance. The initialize() method is interesting because the dynamic proxy is created and returned there. initialize() is shown below:
private Object initialize(Class[] interfaces)
{
return(java.lang.reflect.Proxy.newProxyInstance(getClass().getClassLoader()
,interfaces,this));
}
Notice the use of the newProxyInstance() method. The only way to create an instance of a dynamic proxy class is to call the static newProxyInstance() method on it -- that is, the java.lang.reflect.Proxy class. The java.lang.reflect.Proxy class provides static methods for creating dynamic proxy classes, and it is also the superclass of all dynamic proxy classes
created by those methods. In other words, it's not only a factory for creating dynamic proxy classes; it is a dynamic proxy
class itself! So, in our example, the SOAP proxy is not the dynamic proxy; rather, the dynamic proxy is actually an instance
of the java.lang.reflect.Proxy class returned by the newProxyInstance static method. As you will see later, the dynamic proxy will actually do all its work through the invoke() method that the SOAP proxy implements. So, how does the dynamic proxy know about the SOAP proxy? Because a reference to the
SOAP proxy was passed into the newProxyInstance() method. This may sound convoluted now, but it should all become clear when you examine the invoke() method.
The class java.lang.reflect.Proxy takes a class loader instance as its first parameter; an interface array to dynamically implement (which is the same array
that the client passed into newInstance()) as its second parameter; and an instance of a class that implements the java.lang.reflect.InvocationHandler interface as its third parameter. Since the SOAP Proxy class implements the InvocationHandler interface, the third parameter is the proxy instance itself (i.e., this). The InvocationHandler interface has one method: invoke(). The Java runtime calls invoke() when any method on one of the dynamic proxy's dynamically implemented interfaces is called. So, for example, when the client
calls the sayHelloTo() method on the dynamic proxy's Hello interface, the Java runtime will call invoke() on the SOAP proxy.
You may have observed that the newInstance() method on the SOAP proxy does not return an instance of the SOAP proxy. Instead, it returns the dynamic proxy that newInstance() just created, which dynamically implements the interface array that the client passed in. The client can type-cast this returned
dynamic proxy into any of the interfaces passed into newInstance() and invoke methods on the dynamic proxy as if the dynamic proxy actually implemented the interface. The relevant portion
of the client is shown below for convenience:
.
.
.
try
{
Class[] interfaces = new Class[] {hello.Hello.class};
Hello hello = (Hello)(Proxy.newInstance("urn:Hello",interfaces));
// Invoke the sayHelloTo method that takes a String name.
System.out.println(hello.sayHelloTo("John"));
// Invoke the sayHelloTo method that takes a Name
JavaBean.
Name theName = new Name();
theName.setName("Mala");
System.out.println(hello.sayHelloTo(theName));
}
.
.
.
Now, let's take a look at the invoke() method, which, according to the above code snippet, will be invoked twice, once for each call to the sayHelloTo() method. In a nutshell, the invoke() method does exactly what each client had to manually do in the examples in Part 2. That includes setting up a Call object with the proper call parameters and any custom mapping that those parameters might require. Since the invoke() method in the SOAP proxy handles all that, the client is released of that burden.
Of the three parameters that the invoke() method receives, only the last two are of interest to us. The second parameter, the Method object, gives the method name that is being invoked. Remember that the invoked method name corresponds to a well-known method
that the service exposes. The invoke() method already has the service's object ID, which was passed in as a parameter to the newInstance() method. Using that information, the invoke() method starts setting up the Call object as follows:
Call call = new Call(); call.setTargetObjectURI(urn); call.setMethodName(m.getName()); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
Now it's time to set the parameters for the remote service invocation. For that, we are interested in the third parameter
of the invoke() method: an argument array passed into the method invoked on the dynamic proxy. The zeroth index is the leftmost argument, the first index is the second from the left, and so on. As an example, if the client called
the sayHelloTo(String name) method, then that array would be an array of one string. The invoke() method processes each element of that array and creates a vector of Parameter objects (just like the client did in Part 2):
java.util.Vector params = new java.util.Vector();
for( int i=0; i<args.length; i++ )
{
if( isSimple(args[i]) || isSimpleArray(args[i]) )
{
params.add(new
Parameter(_paramName+(i+1),args[i].getClass(),args[i],null));
}
else if( isVector(args[i]) )
{
addMapping((java.util.Vector)args[i]);
params.add(new
Parameter(_paramName+(i+1),args[i].getClass(),args[i],null));
}
// if this is an non-simple array then
// Assume that this is an array of beans
else if( isArray(args[i]) )
{
if( smr == null )
smr = new
SOAPMappingRegistry();
if( beanSer == null )
beanSer = new
BeanSerializer();
//System.out.println("Adding a default
mapping");
ArraySerializer arraySer = new
ArraySerializer();
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
null, null, beanSer, beanSer);
smr.mapTypes(Constants.NS_URI_SOAP_ENC,null,args[i].getClass(), arraySer,
arraySer);
params.add(new
Parameter(_paramName+(i+1),args[i].getClass(),args[i],null));
}
// Assume that this is a bean
else
{
if( smr == null )
smr = new
SOAPMappingRegistry();
if( beanSer == null )
beanSer = new
BeanSerializer();
String qnamePart =
args[i].getClass().getName();
//System.out.println("qnamePart = " +
qnamePart);
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName(urn,
qnamePart),args[i].getClass(), beanSer,
beanSer);
params.add(new
Parameter(_paramName+(i+1),args[i].getClass(),args[i],null));
}
}
invoke() uses a number of private helper methods, such as isSimple(), to determine the type of parameter. If the parameter is a JavaBean or an array, then a custom SOAP mapping registry must
be set up and set in the Call object via the setSOAPMappingRegistry() method (see Part 2). The SOAP proxy assumes that, in the JavaBean case, any JavaBean used by the SOAP service maps as follows: the NameSpace UR field is set to the object ID and the Local Part is set to the JavaBean's fully qualified class name. Since this is exactly how we've deployed the HelloWorld service, we're
fine.