package zutil.network.http.soap; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import javax.wsdl.Binding; import javax.wsdl.BindingInput; import javax.wsdl.BindingOperation; import javax.wsdl.BindingOutput; import javax.wsdl.Definition; import javax.wsdl.Fault; import javax.wsdl.Import; import javax.wsdl.Input; import javax.wsdl.Message; import javax.wsdl.Operation; import javax.wsdl.Output; import javax.wsdl.Part; import javax.wsdl.Port; import javax.wsdl.PortType; import javax.wsdl.Service; import javax.wsdl.WSDLException; import javax.wsdl.extensions.soap.SOAPAddress; import javax.wsdl.extensions.soap.SOAPBinding; import javax.wsdl.extensions.soap.SOAPBody; import javax.wsdl.extensions.soap.SOAPHeader; import javax.wsdl.extensions.soap.SOAPOperation; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLWriter; import javax.xml.namespace.QName; import zutil.network.http.HttpPage; import zutil.network.http.HttpPrintStream; import zutil.network.http.soap.SOAPInterface.WSDLDocumentation; import zutil.network.http.soap.SOAPInterface.WSDLParamDocumentation; import zutil.network.http.soap.SOAPObject.SOAPFieldName; import zutil.MultiPrintStream; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.Namespace; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; import org.xml.sax.SAXException; import com.ibm.wsdl.extensions.PopulatedExtensionRegistry; import com.ibm.wsdl.extensions.soap.SOAPConstants; import com.sun.org.apache.xerces.internal.dom.DocumentImpl; /** * This is an HTTPPage for the HTTPServer that * handles soap messages. * * TODO: Read SOAPObjects as input parameter * TODO: Ability to have multiple arrays of same SOAPObject * * Features: * Input: *
-int *
-double *
-float *
-char *
-String *
-byte[] *
-And the Wrappers except byte * * Output: *
-SOAPObjects *
-byte[] *
-int *
-double *
-float *
-char *
-String *
-Arrays of Output *
-And the Wrappers except byte * * @author Ziver */ public class SOAPHttpPage implements HttpPage{ // valid methods for this soap page private HashMap methods; // contains an method and the names for the parameters private class MethodChasch{ String[] paramName; boolean[] paramOptional; String returnName; Method method; boolean header; MethodChasch(Method m){ method = m; paramName = new String[method.getParameterTypes().length]; paramOptional = new boolean[method.getParameterTypes().length]; header = false; } } // The object that the functions will be invoked from private SOAPInterface interf; // The WSDL document private Definition wsdl; // The WSDL Type part private Document wsdlType; // the URL to this soap page private String url; // Session enabled private boolean session_enabled; public SOAPHttpPage(String url, SOAPInterface interf) throws WSDLException{ //if(!SOAPInterface.class.isAssignableFrom(interf) ) // throw new ClassCastException("Class does not implement SOAPInterface!"); this.url = url; this.interf = interf; this.session_enabled = false; methods = new HashMap(); for(Method m : interf.getClass().getDeclaredMethods()){ // check for public methods if((m.getModifiers() & Modifier.PUBLIC) > 0 && !m.isAnnotationPresent(SOAPInterface.SOAPDisabled.class)){ MethodChasch chasch = new MethodChasch(m); StringBuffer tmp = new StringBuffer(m.getName()+"("); // Get the parameter names Annotation[][] paramAnnotation = m.getParameterAnnotations(); for(int i=0; i client_info, HashMap session, HashMap cookie, HashMap request) { try { out.setHeader("Content-Type", "text/xml"); out.flush(); if(request.containsKey("wsdl")){ WSDLWriter writer = WSDLFactory.newInstance().newWSDLWriter(); writer.writeWSDL(wsdl, out); } else if(request.containsKey("type")){ OutputFormat format = OutputFormat.createPrettyPrint(); XMLWriter writer = new XMLWriter( out, format ); writer.write( wsdlType ); } else{ SOAPInterface obj = null; if(session_enabled && session.containsKey("SOAPInterface")) obj = (SOAPInterface)session.get("SOAPInterface"); else{ obj = interf.getClass().newInstance(); if(session_enabled) session.put("SOAPInterface", obj); } Document document = soapResponse( request.get(""), obj); OutputFormat format = OutputFormat.createPrettyPrint(); XMLWriter writer = new XMLWriter( out, format ); writer.write( document ); } } catch (Exception e) { e.printStackTrace(MultiPrintStream.out); } } /** * Generates a soap response for the given * @param xml is the XML request * @return a Document with the response */ public Document soapResponse(String xml){ try { return soapResponse(xml, interf.getClass().newInstance()); } catch (Exception e) { e.printStackTrace(); } return null; } protected Document soapResponse(String xml, SOAPInterface obj){ try { Document document = DocumentHelper.createDocument(); Element envelope = document.addElement("soap:Envelope"); envelope.add(new Namespace("soap", "http://www.w3.org/2001/12/soap-envelope")); envelope.addAttribute("soap:encodingStyle", "http://www.w3.org/2001/12/soap-encoding"); Element header = envelope.addElement( "soap:Header" ); Element body = envelope.addElement( "soap:Body" ); try{ Element request = getXMLRoot(xml); // Header if( request.element("Header") != null){ prepareInvoke( obj, request.element("Header"), header ); } // Body if( request.element("Body") != null){ prepareInvoke( obj, request.element("Body"), body ); } }catch(Throwable e){ body.clearContent(); Element fault = body.addElement("soap:Fault"); // The fault source if(e instanceof SOAPClientException || e instanceof SAXException || e instanceof DocumentException) fault.addElement("faultcode").setText( "soap:Client" ); else fault.addElement("faultcode").setText( "soap:Server" ); // The fault message if( e.getMessage() == null || e.getMessage().isEmpty()) fault.addElement("faultstring").setText( ""+e.getClass().getSimpleName() ); else fault.addElement("faultstring").setText( ""+e.getMessage() ); e.printStackTrace(MultiPrintStream.out); } return document; } catch (Exception e) { e.printStackTrace(MultiPrintStream.out); } return null; } /** * Takes an XML Element and invokes all the * chide Elements as methods. * * @param obj is the object that the methods will be called from * @param requestRoot is the Element where the children lies * @param responseRoot is the root element of the response */ @SuppressWarnings("unchecked") private void prepareInvoke(Object obj, Element requestRoot, Element responseRoot) throws Throwable{ Iterator it = requestRoot.elementIterator(); while( it.hasNext() ){ Element e = it.next(); if(methods.containsKey(e.getQName().getName())){ MethodChasch m = methods.get(e.getQName().getName()); Object[] params = new Object[m.paramName.length]; // Get the param values for(int i=0; i c) throws IOException{ if(data == null || data.isEmpty()) return null; if( c == String.class) return data; else if(c == Integer.class) return Integer.parseInt(data); else if(c == int.class) return Integer.parseInt(data); else if(c == Long.class) return Long.parseLong(data); else if(c == long.class) return Long.parseLong(data); else if(c == Float.class) return Float.parseFloat(data); else if(c == float.class) return Float.parseFloat(data); else if(c == Double.class) return Double.parseDouble(data); else if(c == double.class) return Double.parseDouble(data); else if(c == Boolean.class) return Boolean.parseBoolean(data); else if(c == boolean.class) return Boolean.parseBoolean(data); else if(c == Byte.class) return Byte.parseByte(data); else if(c == byte.class) return Byte.parseByte(data); else if(byte[].class.isAssignableFrom(c)) return new sun.misc.BASE64Decoder().decodeBuffer(data); return null; } /** * Invokes a specified method * * @param m is the function * @param params a vector with arguments * @throws Throwable */ protected Object invoke(Object obj, Method m, Object[] params) throws Throwable{ try { return m.invoke(obj, params ); } catch (IllegalArgumentException e) { throw new SOAPClientException("Arguments missing for "+m.getName()+"!"); } catch (IllegalAccessException e) { throw e; } catch (InvocationTargetException e) { throw e.getCause(); } } /** * Converts an String XML to an Element * * @param msg is the string XML * @return the XML root Element */ private Element getXMLRoot(String xml) throws Exception { if(xml != null && !xml.isEmpty()){ Document document = DocumentHelper.parseText(xml); return document.getRootElement(); } return null; } private String getClassSOAPName(Class c){ Class cTmp = getClass(c); if(byte[].class.isAssignableFrom(c)){ return "base64Binary"; } else if( SOAPObject.class.isAssignableFrom(cTmp) ){ return c.getSimpleName(); } else{ String ret = c.getSimpleName().toLowerCase(); if(cTmp == Integer.class) ret = ret.replaceAll("integer", "int"); else if(cTmp == Character.class)ret = ret.replaceAll("character", "char"); return ret; } } /** * Generates an WSDL document for the class * * @throws WSDLException */ private void generateWSDL() throws WSDLException{ ArrayList> types = new ArrayList>(); String tns = url+"?wsdl"; String xsd = "http://www.w3.org/2001/XMLSchema"; String soap = "http://schemas.xmlsoap.org/wsdl/soap/"; String wsdln = "http://schemas.xmlsoap.org/wsdl/"; String td = url+"?type"; PopulatedExtensionRegistry extReg = new PopulatedExtensionRegistry(); WSDLFactory factory = WSDLFactory.newInstance(); String portTypeName = this.interf.getClass().getSimpleName()+"PortType"; wsdl = factory.newDefinition(); wsdl.setQName(new QName(tns, this.interf.getClass().getSimpleName())); wsdl.setTargetNamespace(tns); wsdl.addNamespace("tns", tns); wsdl.addNamespace("xsd", xsd); wsdl.addNamespace("soap", soap); wsdl.addNamespace("wsdl", wsdln); wsdl.addNamespace("td", td); Message exception = wsdl.createMessage(); exception.setQName(new QName(tns, "exception")); exception.setUndefined(false); Part epart = wsdl.createPart(); epart.setName("message"); epart.setTypeName(new QName(xsd, "string")); exception.addPart(epart); wsdl.addMessage(exception); // Types import Import imp = wsdl.createImport(); imp.setNamespaceURI(td); imp.setLocationURI(td); wsdl.addImport(imp); // PrtType PortType portType = wsdl.createPortType(); portType.setQName(new QName(tns, portTypeName)); portType.setUndefined(false); for(MethodChasch m : methods.values()){ Operation operation = wsdl.createOperation(); //********* Request Messages if(m.paramName.length > 0){ Message msgIn = wsdl.createMessage(); msgIn.setQName(new QName(tns, m.method.getName()+"Request")); msgIn.setUndefined(false); //***** Documentation WSDLParamDocumentation tmpParamDoc = m.method.getAnnotation(SOAPInterface.WSDLParamDocumentation.class); if(tmpParamDoc != null){ org.w3c.dom.Document xmldoc= new DocumentImpl(); org.w3c.dom.Element paramDoc = xmldoc.createElement("wsdl:documentation"); paramDoc.setTextContent(tmpParamDoc.value()); msgIn.setDocumentationElement(paramDoc); } // Parameters for(int i=0; i cTmp = getClass(m.method.getReturnType()); if(byte[].class.isAssignableFrom(m.method.getReturnType())){ part.setTypeName(new QName(xsd, "base64Binary")); } // is an array? else if(m.method.getReturnType().isArray()){ part.setTypeName(new QName(td, "ArrayOf"+getClassSOAPName(m.method.getReturnType()).replaceAll("[\\[\\]]", ""))); // add to type generation list if(!types.contains(m.method.getReturnType())) types.add(m.method.getReturnType()); } else if( SOAPObject.class.isAssignableFrom(cTmp) ){ // its an SOAPObject part.setTypeName(new QName(td, getClassSOAPName(m.method.getReturnType()))); // add to type generation list if(!types.contains(cTmp)) types.add(cTmp); } else{// its an Object part.setTypeName(new QName(xsd, getClassSOAPName(m.method.getReturnType()))); } wsdl.addMessage(msgOut); Output output = wsdl.createOutput(); output.setMessage(msgOut); operation.setOutput(output); } //************* Exceptions if(m.method.getExceptionTypes().length <= 0){ Fault fault = wsdl.createFault(); fault.setMessage(exception); operation.addFault(fault); } //************* Operations operation.setName(m.method.getName()); operation.setUndefined(false); //***** Documentation WSDLDocumentation tmpDoc = m.method.getAnnotation(SOAPInterface.WSDLDocumentation.class); if(tmpDoc != null){ // org.w3c.dom.Document xmldoc= new DocumentImpl(); org.w3c.dom.Element doc = xmldoc.createElement("wsdl:documentation"); doc.setTextContent(tmpDoc.value()); operation.setDocumentationElement(doc); } portType.addOperation(operation); } wsdl.addPortType(portType); // Binding Binding binding = wsdl.createBinding(); binding.setQName(new QName(tns, interf.getClass().getSimpleName()+"Binding")); binding.setPortType(portType); binding.setUndefined(false); SOAPBinding soapBinding = (SOAPBinding)extReg.createExtension(Binding.class, SOAPConstants.Q_ELEM_SOAP_BINDING); soapBinding.setStyle("rpc"); //soapBinding.setRequired(true); soapBinding.setTransportURI("http://schemas.xmlsoap.org/soap/http"); binding.addExtensibilityElement(soapBinding); for(MethodChasch m : methods.values()){ BindingOperation operation = wsdl.createBindingOperation(); operation.setName(m.method.getName()); SOAPOperation soapOperation = (SOAPOperation)extReg.createExtension(BindingOperation.class, SOAPConstants.Q_ELEM_SOAP_OPERATION); soapOperation.setSoapActionURI(""); operation.addExtensibilityElement(soapOperation); // input if(m.paramName.length > 0){ BindingInput input = wsdl.createBindingInput(); // Header if(m.header){ SOAPHeader soapHeader = (SOAPHeader)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_HEADER); soapHeader.setUse("literal"); input.addExtensibilityElement(soapHeader); }// Body else{ SOAPBody soapBody = (SOAPBody)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_BODY); soapBody.setUse("literal"); input.addExtensibilityElement(soapBody); } operation.setBindingInput(input); } // output if(!m.method.getReturnType().equals( void.class )){ BindingOutput output = wsdl.createBindingOutput(); // Header if(m.header){ SOAPHeader soapHeader = (SOAPHeader)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_HEADER); soapHeader.setUse("literal"); output.addExtensibilityElement(soapHeader); }// Body else{ SOAPBody soapBody = (SOAPBody)extReg.createExtension(BindingInput.class, SOAPConstants.Q_ELEM_SOAP_BODY); soapBody.setUse("literal"); output.addExtensibilityElement(soapBody); } operation.setBindingOutput(output); } binding.addBindingOperation(operation); } wsdl.addBinding(binding); // Service Port port = wsdl.createPort(); port.setName( interf.getClass().getSimpleName()+"Port" ); port.setBinding(binding); SOAPAddress addr = (SOAPAddress)extReg.createExtension(Port.class, SOAPConstants.Q_ELEM_SOAP_ADDRESS); addr.setLocationURI(url); port.addExtensibilityElement(addr); Service ser = wsdl.createService(); ser.setQName(new QName(tns, interf.getClass().getSimpleName()+"Service")); ser.addPort(port); wsdl.addService(ser); // generate the complexTypes generateWSDLType(types); } /** * This function generates the Type part of the WSDL. * Should be cabled after generateWSDL has finished. * */ private void generateWSDLType(ArrayList> types){ wsdlType = DocumentHelper.createDocument(); Element definitions = wsdlType.addElement( "wsdl:definitions" ); definitions.addAttribute("targetNamespace", url+"?type"); definitions.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema"); definitions.addNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/"); definitions.addNamespace("SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"); Element typeE = definitions.addElement("wsdl:types"); Element schema = typeE.addElement("xsd:schema"); for(int n=0; n c = types.get(n); // Generate Array type if(c.isArray()){ Class ctmp = getClass(c); Element type = schema.addElement("xsd:complexType"); type.addAttribute("name", "ArrayOf"+getClassSOAPName(c).replaceAll("[\\[\\]]", "")); Element complexContent = type.addElement("complexContent"); Element restriction = complexContent.addElement("restriction"); restriction.addAttribute("base", "SOAP-ENC:Array"); Element attribute = restriction.addElement("attribute"); attribute.addAttribute("ref", "SOAP-ENC:arrayType"); attribute.addAttribute("wsdl:arrayType", "tns:"+getClassSOAPName(c)); if(!types.contains(ctmp)) types.add(ctmp); } // Generate SOAPObject type else if(SOAPObject.class.isAssignableFrom(c)){ Element type = schema.addElement("xsd:complexType"); type.addAttribute("name", getClassSOAPName(c)); Element sequence = type.addElement("xsd:sequence"); Field[] fields = c.getFields(); for(int i=0; i cTmp = getClass(fields[i].getType()); if(SOAPObject.class.isAssignableFrom(cTmp)){ element.addAttribute("type", "tns:"+getClassSOAPName(cTmp)); if(!types.contains(cTmp)) types.add(cTmp); } else{ element.addAttribute("type", "xsd:"+getClassSOAPName(fields[i].getType())); } // Is the Field optional if(tmp != null && tmp.optional()) element.addAttribute("minOccurs", "0"); } } } } private Class getClass(Class c){ if(c.isArray()){ return getClass(c.getComponentType()); } return c; } }