Java dynamic class loading issue
Resource:
The thread to discuss how to add (only) jar files into JVM just like it is added in classpath during start up.
http://forum.java.sun.com/thread.jspa?threadID=300557&start=0&tstart=0
This link talks about how to dynamically plug in/out jar files (the best way)
http://java.sun.com/products/jndi/tutorial/beyond/misc/index.html
A more detailed white paper is available at
http://www.javageeks.com/Papers/ClassForName/index.html
Quoted abstract:
Abstract:
Dynamic loading of Java classes at runtime provides tremendous flexibility in the development of enterprise systems. It provides for the basis of "application servers", and allows even simpler, lighter-weight systems to accomplish some of the same ends. Within Java, dynamic loading is typically achieved by calling the forName method on the class java.lang.Class; however, when Class.forName is naively called from within an Extension, strange errors can occur. This paper describes why those errors occur, and how Java2 provides two facilities, one a modification to the forName syntax, and the other, called the "Thread context ClassLoader", to avoid them. This paper assumes you are passingly familiar with Java Reflection, and have at least a general idea of what Java ClassLoaders are and how they are used within Java code.
Keywords:Java ClassLoaders Extensions Threads
Following is the core code for invokeMethod
public ReturnValue invokeMethod(
String moduleName, String className, String methodName, Object[] methodParams)
{
//!!!!!check moduleName/className/methodName, undone
ClassLoader prevCl = Thread.currentThread().getContextClassLoader();
ReturnValue retVal = new ReturnValue();
retVal.success = false;
try {
File cwd = new File (workDir);
System.out.println("current work dir: "+cwd.getCanonicalPath());
String fileSeparator = System.getProperty("file.separator");
String xsharpCodePackageLocation = cwd.getCanonicalPath()+fileSeparator+moduleName+".jar";
File xsharpJar = new File(xsharpCodePackageLocation);
File implJar = new File(implAssemblyLocation);
// add .jar to class loader
URL[] urls = new URL[1];
urls[0]=xsharpJar.toURL();
ClassLoader urlCl = URLClassLoader.newInstance(urls,prevCl);
Thread.currentThread().setContextClassLoader(urlCl);
Class thisClass = Class.forName(moduleName+"."+className,true,urlCl);
Object iClass = thisClass.newInstance();
Method m[] = thisClass.getDeclaredMethods();
for (int i=0; i<m.length;i++)
{
if (methodName.equals(m[i].getName()))
{
m[i].setAccessible(true);
retVal.result = m[i].invoke(iClass, methodParams);
retVal.success = true;
}
}
}
catch (Exception e) {
retVal.info = e.toString();
}finally
{
Thread.currentThread().setContextClassLoader(prevCl);
}
return retVal;
}
gt4 and jwsdp-1.5 jar difference list
In GT4 grid service implementation, in order to use client stub generated by jwsdp1.5, we have to copy the following jar files from jwsdp1.5 to gt4.
Details:
${jwsdp1.5}/jaxrpc/lib/jaxrpc-api.jar
${jwsdp1.5}/jaxrpc/lib/jaxrpc-impl.jar
${jwsdp1.5}/jaxrpc/lib/jaxrpc-spi.jar
${jwsdp1.5}/jwsdp-shared/lib/activation.jar
${jwsdp1.5}/jwsdp-shared/lib/mail.jar
${jwsdp1.5}/saaj/lib/saaj-api.jar
${jwsdp1.5}/saaj/lib/saaj-impl.jar
WSRF.NET trouble list
The root cause why in WSRF.NET we can not use object, and object[] is due to the wrong type and element name generated by WSRF.NET
In generated MCWSRFPortType.wsdl
Change tns:Object to xsd:anyType
Change name=”Object” to name=”anyType”
----------Original -----------------
<xsd:complexType name="Object" />
<xsd:complexType name="ArrayOfObject">
<xsd:sequence>
<xsd:element type="tns:Object" name="Object" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReturnValue">
<xsd:sequence>
<xsd:element type="xsd:boolean" name="success" />
<xsd:element type="tns:Object" name="result" />
<xsd:element type="xsd:string" name="info" />
</xsd:sequence>
</xsd:complexType>
----------Changed -----------------
<xsd:complexType name="ArrayOfObject">
<xsd:sequence>
<xsd:element type="xsd:anyType" name="anyType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ReturnValue">
<xsd:sequence>
<xsd:element type="xsd:boolean" name="success" />
<xsd:element type="xsd:anyType" name="result" />
<xsd:element type="xsd:string" name="info" />
</xsd:sequence>
</xsd:complexType>
Then update the web reference on the client side (Do not change service side)
Play with java SOAPElement in JAX-RPC xsd:anyType
In JAX_RPC, xsd:anyType is mapped to javax.xml.soap.SOAPElement
Here is the java function to convert an int to SOAPElement
public static SOAPElement int2SOAPElement(String tagName, int value)
{
try{
SOAPFactory sf = SOAPFactory.newInstance();
SOAPElement e = sf.createElement(tagName);
String xsi = "http://www.w3.org/2001/XMLSchema-instance";
Name xsiTypeString = sf.createName("type", "xsi", xsi);
e.addAttribute( xsiTypeString, "xsd:int" );
e.addTextNode((new Integer(value)).toString());
return e;
}catch(Exception e)
{
System.out.println ( "int2SOAPElement, exceptioin: "+ e.toString() );
return null;
}
}
For example
<!-- method input: array of object -->
<xsd:complexType name="ArrayOfObject">
<xsd:sequence>
<xsd:element type="xsd:anyType" name="Object" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<!-- invoke mobile code -->
<xsd:element name="invokeMethod">
<xsd:complexType>
<xsd:sequence>
<xsd:element type="xsd:string" name="moduleName" />
<xsd:element type="xsd:string" name="methodName" />
<xsd:element type="tns:ArrayOfObject" name="methodParams" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
String MODULE = "CmmCode.Math";
String METHOD = "main_entry";
ArrayOfObject aoo= new ArrayOfObject();
SOAPElement e = int2SOAPElement("Object",1234);
aoo.setObject(new SOAPElement[]{e});
retVal = service.invokeMethod(MODULE, METHOD, aoo );
the result xml is
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns0="http://www.cs.binghamton.edu/mcws">
<env:Body>
<ns0:invokeMethod>
<moduleName>CmmCode.Math</moduleName>
<methodName>main_entry</methodName>
<methodParams>
<Object xsi:type="xsd:int">1234</Object>
</methodParams>
</ns0:invokeMethod>
</env:Body>
</env:Envelope>
How to use Java SOAPElement
1. javax.xml.soap
à
saaj.jar
Interface SOAPElement
http://java.sun.com/j2ee/1.4/docs/api/javax/xml/soap/SOAPElement.html
This package is defined in the SOAP with Attachments API for JavaTM (SAAJ) 1.1 specification.
SOAPElement could be used as Element
Because
public interface SOAPElement
extends Node, Element
2. Using xsd:anyType to Represent XML Documents in a Service Interface: Design Details
https://bpcatalog.dev.java.net/nonav/soa/doc-anytype/design.html
public class PurchaseOrder {
private String poId;
private Calendar createDate;
private Address shipTo;
private Address billTo;
private LineItem[] items;
public PurchaseOrder() {}
public PurchaseOrder(String poId, Calendar createDate,
Address shipTo, Address billTo, LineItem[] items) {
this.poId = poId;
this.shipTo = shipTo;
this.createDate = createDate;
this.billTo = billTo;
this.items = items;
}
...
public SOAPElement toXMLSOAPElement(boolean wrapper) {
SOAPElement soapElem = null;
try {
//construct the DOM tree
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element poElem = doc.createElement("PurchaseOrder");
if(wrapper){
Element docElem = doc.createElement("BusinessDocumentRequest");
doc.appendChild(docElem);
docElem.appendChild(poElem);
} else{
doc.appendChild(poElem);
}
Element elem = doc.createElement("poId");
elem.appendChild(doc.createTextNode(poId));
poElem.appendChild(elem);
elem = doc.createElement("createDate");
elem.appendChild(doc.createTextNode((new SimpleDateFormat("MM-dd-yy")).format(createDate.getTime())));
poElem.appendChild(elem);
elem = doc.createElement("shipTo");
poElem.appendChild(shipTo.toDOM(doc, elem));
elem = doc.createElement("billTo");
poElem.appendChild(billTo.toDOM(doc, elem));
for(int i = 0; i < items.length; ++i){
poElem.appendChild(items[i].toDOM(doc));
}
//create a SOAPElement
SOAPElement parent = SOAPFactory.newInstance().createElement("dummy");
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.transform(new DOMSource(doc), new DOMResult(parent));
soapElem = (SOAPElement) parent.getChildElements().next();
} catch(TransformerException te) {
te.printStackTrace(System.err);
throw new RuntimeException(te.getMessage(), te);
} catch(ParserConfigurationException pce) {
pce.printStackTrace(System.err);
throw new RuntimeException(pce.getMessage(), pce);
} catch(SOAPException se) {
se.printStackTrace(System.err);
throw new RuntimeException(se.getMessage(), se);
}
return soapElem;
}
Java web service portal
1. Patterns and Strategies for Building Document-Based Web Services
http://java.sun.com/developer/technicalArticles/xml/jaxrpcpatterns/index.html
2. Writing A JAX-RPC Client from WSDL (quite old though, but the build.xml is useful)
http://www.descriptor.com/articles/web-services/wsdlclient.html
Here is my tutorial
1. get the wsdl file in place ../wsdl/mcws.wsdl
2. use the following bat to generate stubs.
C:\tomcat50-jwsdp\jaxrpc\bin\wscompile -gen:client -d stub config.xml –keep
3. generate the config.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location="../wsdl/mcws.wsdl" packageName="mcwsClient"/>
</configuration>
4. create the ant build.xml
<?xml version="1.0"?>
<project default="run-client">
<!-- define the destination directories -->
<property name="jwsdp-home"
value="C:/tomcat50-jwsdp"/>
<property name="jaxrpc-home"
value="${jwsdp-home}/jaxrpc"/>
<property name="saaj-home"
value="${jwsdp-home}/saaj"/>
<!-- setup the CLASSPATH -->
<path id="classpath">
<fileset dir="${jwsdp-home}/common/lib">
<include name="*.jar" />
</fileset>
<fileset dir="${jaxrpc-home}/lib">
<include name="*.jar" />
</fileset>
<fileset dir="${saaj-home}/lib">
<include name="*.jar" />
</fileset>
<fileset dir="${jwsdp-home}/jwsdp-shared/lib">
<include name="*.jar" />
</fileset>
<fileset file="${jwsdp.home}/fastinfoset/lib/FastInfoset.jar"/>
<fileset file="${jwsdp.home}/sjsxp/lib/jsr173_api.jar"/>
</path>
<!-- define the "build" target -->
<target name="build">
<javac srcdir="."
destdir="./classes" >
<classpath refid="classpath"/>
<classpath>
<pathelement location="."/>
</classpath>
</javac>
</target>
<!-- define the "run-client" target -->
<target name="run-client" depends="build">
<java classname="Client" fork="true">
<classpath refid="classpath"/>
<classpath>
<pathelement location="./classes"/>
</classpath>
</java>
</target>
<!-- define the "run" target -->
<target name="run" >
<java classname="Client" fork="true">
<classpath refid="classpath"/>
<classpath>
<pathelement location="./classes"/>
</classpath>
</java>
</target>
</project>
5. build client
C:\> ant build
6. run client
C:\> ant run
interoperability between GSOAP and .NET
The key point is about namespace.
1. How to use gsoap DOM parser to generate xsd:anyType node
extern "C" ACE_Svc_Export soap_dom_element factory()
{
xsd__int ret = main_entry();
struct soap soap;
soap_init(&soap);
soap_set_imode(&soap, SOAP_DOM_NODE);
xsd__int domval=ret ; //we have to use this type
soap_dom_element retDomVal(&soap, "mcws", NULL, &domval, SOAP_TYPE_xsd__int);
//”mcws” ----- must use the namespace which is the same defined in WSDL
// NULL ------ must be NULL else this name will be used as TAG name in the soap message
return retDomVal;
}
2. the result soap message generated by GSOAP, which is exactly specified in WSDL. Such that .NET client can parse it out correctly.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:mcws="http://www.binghamton.edu/mcwsDocu">
<SOAP-ENV:Body>
<mcws:invokeCodeResponse>
<mcws:value xsi:type="xsd:int">49</mcws:value>
<mcws:ret>0</mcws:ret>
</mcws:invokeCodeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
3. GSOAP wsdl
<message name="installCodeResponse">
<part name="parameters" element="mcws:installCodeResponse"/>
</message>
<element name="invokeCodeResponse">
<complexType>
<sequence>
<element name="value" type="xsd:anyType" minOccurs="1" maxOccurs="1"/>
<element name="ret" type="xsd:int" minOccurs="1" maxOccurs="1"/>
</sequence>
</complexType>
</element>
4. Debugging && trouble shooting
4.1 on GSOAP side, define DEBUG in the compiler for example –DDEBUG
Then we will get SEND.log and RECV.log in the current working directory
4.2 on DOTNET side, we can add code into client stub (generated by wsdl.exe or “update web reference”
Override public partial class McwsDocu : System.Web.Services.Protocols.SoapHttpClientProtocol
….
protected override XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize)
Add references
using System.Net;
using System.IO;
using System.Xml;
using System.Text;
Implementation
/* copy stream to another stream*/
private void Copy(Stream streamFrom, Stream streamTo)
{
TextReader reader = new StreamReader(streamFrom);
TextWriter writer = new StreamWriter(streamTo);
String line;
line = reader.ReadLine();
while (line != null)
{
writer.WriteLine(line);
line = reader.ReadLine();
}
writer.Flush();
}
/* will generate log file for incoming soap message to c:\\log.txt */
protected override XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize)
{
MemoryStream m = new MemoryStream();
Copy(message.Stream, m);
m.Seek(0, SeekOrigin.Begin);
Double now = System.DateTime.Now.ToOADate();
FileStream fs = new FileStream("c:\\log.txt", FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter w = new StreamWriter(fs); // create a Char writer
w.BaseStream.Seek(0, SeekOrigin.End); // set the file pointer to the end
TextReader reader = new StreamReader(m);
String line;
while ((line = reader.ReadLine()) != null)
{
w.WriteLine(line);
}
w.Flush();
w.Close();
m.Seek(0, SeekOrigin.Begin);
XmlTextReader xmlreader;
xmlreader = new XmlTextReader(m);
xmlreader.ProhibitDtd = true;
xmlreader.Normalization = true;
xmlreader.XmlResolver = null;
return xmlreader;
}