|
YOUR FEEDBACK Did you read today's front page stories & breaking news?
SYS-CON.TV |
TODAY'S TOP SOA & WEBSERVICES LINKS Product Review SOAP on a ROPE - Requested Objects Passed Elegantly
SOAP on a ROPE - Requested Objects Passed Elegantly
By: Wyn Easton
Jan. 11, 2002 12:00 AM
Converting a Web site to a Web service requires some new tools in your programming toolbox. For Web services that need to exchange arbitrary Java objects, you can add the tools described here. I'll show you how to exchange Java objects using SOAP RPC and invoke methods on these objects. The source code for the examples as well as the listings discussed here can be downloaded from www.sys-con.com/webservices/sourcec.cfm. The example code consists of a client and a simple Web service (see Figure 1). The client is a Java application named Client. The Web service code is a Java class called ObjectDepot. ObjectDepot exposes two SOAP callable methods using a deployment descriptor named DeploymentDescriptor.xml. The method getMyObject() will return an instance of AClass. The method getObject (String name) returns an instance of a named class that is passed as a String parameter. BClass is used in the example. AClass and BClass are simple do-nothing classes I've used for demonstration purposes. The only restriction on the objects that you send and receive is that they must implement the Serializable Interface. The client makes a SOAP method call that returns a Java Object. The dotted lines represent the marshall() and by ObjSerializer. unmarshall() method calls made I used the following tools in addition to the Java 2 Platform, Standard Edition (J2SE) to set up my development environment:
A Little Background Another type mapping is used to determine how the data should be unmarshalled. The type mappings are stored in a type-mapping registry. The default registry is org.apache. soap. encoding. SOAPMappingRegistry. Apache's SOAP implementation contains serializers and deserializers that will marshall and unmarshall many of the basic Java objects. Among the objects supported is the Java String Object, and there is even a Java Bean serializer and deserializer. After browsing the SOAP documentation, I wondered why any Java object couldn't be transmitted. This was in the Apache SOAP documentation: "If you need to create a new serializer/ deserializer, then looking at the source code for the predefined ones will probably provide you with the best guidance. Remember that they will need to implement org.apache .soap. util.xml. Seri alizer and org.apa che.soap. util.xml. Deserializer respectively. You can implement them both in the same class or use two different classes. It really makes no difference." There was my answer. I could write my own Java object serializ er/deserializer. After digging a little further, I discovered that there are several technical hurdles to scale. First I needed to serialize the object. Then I'd need to fit the serialized object into the SOAP envelope. Once the object reached the client, it would need to be deserialized so that it could be used. The biggest hurdle was getting access to the class file on the client so that the object could be deserialized.
My Approach To get started, I needed a serializer and deserializer for my Java objects. My implementation of both is contained in Obj Serializer.java. Objserializer implements the Apache SOAP serializer and deserializer interfaces. I used Java's standard Object Input Stream and ObjectOutputStream to do most of the heavy lifting (see Listing 1). I also used an Apache SOAP utility called Base64 to convert the binary object data to a base64 encoded String.
byte byteArray[] = ObjectToArray(value); I did this to prevent any encoding that an application server might try to do.
Wire Tapping As mentioned earlier, I saw that the deserializer on the client machine had to have access to the object's class file for it to be deserialized for the client to use. When I began, the only way I could accomplish this was to copy the class file from the Web server to a file in the class path of the client just before the class file was needed for unmarshalling the data into the object. I added some code to the catch block in ObjSerializer that caught the ClassNotFoundException that was being thrown by the readObject() method of ObjectIn putStream (see Listing 2). In the catch block I retrieved the class file from the Web server and then retried the unmarshalling. After the readObj ect() method returned the object, I removed the class file from the disk. I left my original unmarshall() method in ObjSerial izer.java for you. The unmarshalling worked and I could use the returned object, but I thought there must be a more elegant way. I posted a note on the Apache SOAP mailing list explaining what I was trying to do and someone suggested I look at class loaders. After a little reading I realized that URLClass Loader would do the job and I only needed to override the findClass() method in URLClassLoader.
A Class Loader Mini-Course When MyURLClassLoader is added to the delegation, the system class loader becomes its parent. When a class loader loads a class, it consults its parent first to give the parent a chance to load the class file. Whenever a class refers to another class, the same class loader that loaded the referencing class loads the referent. In other words, since ObjSerializer is loaded by MyURLClassLoader, MyURLClass Loader will be used to load classes needed by ObjSerializer. That is done when the system class loader calls the findClass() method of MyURLClassLoader after failing to find a class file when the readObject() method is called from ObjSerializer's unmarshall() method (see Figure 3). The findClass() method needs to know the host name of the Web server and where the class files are located on the server. These are read from ObjSerial izer.properties when MyUrlClassLoa der is instantiated. The unmarshall() method calls the readObject() method of Object Input Stream. Which then calls the find Class() method of MyURLClass Loader. The findClass() method retrieves the class file from the remote web server. The readObject() method returns the object to the unmarshall() method. The unmarshall() method returns the object in a SOAP Bean. Here are the contents of my ObjSerializer. properties file:
ClassDefHost=localhost
As I explained earlier, I would need a Class that would load ObjSerializer using the Class.forName() method. That way I could pass an instance of MyURLClass Loader to be used to load ObjSerializer. This project was getting a little complicated, but I was almost home. I created ObjSerializerLoader.java to do the job (see Listing 4). Okay, now I was ready. I had my SOAP deployment descriptor set up to register ObjSerializerLoader as my serializer and deerializer on the SOAP server. I also registered ObjSerializer Loader in Client for the same purpose. I started up Tomcat, ran Client which invoked ObjDepot's getMyObject() method through SOAP RPC. I was upset to see a ClassNotFound Exception, just as I had seen before. It looked like MyURLClass Loader wasn't being used at all. After a few System. out.println() statements, I discovered I was right. MyURLClassLoa der wasn't being used. As it turned out, I had made only one mistake. When I used the Class.f or Name() method to create my instance of ObjSerializer, I assigned it to a variable of type ObjSerializer. Since the system class loader could find the class file for ObjSerializer, the system class loader was used to create the object and not my class loader. What I needed to do was put ObjSerializer into a variable of type Object and use Java Reflection to invoke the marshal() and unmarshall() methods (see Listing 5). That way there would be no reference to ObjSerializer in ObjSerializerLoader, so my code would compile without ObjSe rializer in the class path and the system class loader would delegate to MyURL ClassLoader's find Class() method when ObjectSerializer is unmarshalling the returned data. As I mentioned earlier, the delegation takes place when the read Object() method is called in the unmar shall() method of ObjSerializer (see Listing 6). If you're like me, you've spent some time figuring out class path problems when some class file couldn't be found. In this case, I didn't want ObjSerializer to be found by the system class loader.
Try, Try Again A word about security: as powerful as this tool can be, it can also be dangerous. This tool should only be used among trusted Web sites. You should also consider encryption, based on the sensitivity of the data transmitted.
Running the Example Code
Create Your Own YOUR FEEDBACK
SOA WORLD LATEST STORIES
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||