diff --git a/.classpath b/.classpath index a6e589d..19e0e62 100644 --- a/.classpath +++ b/.classpath @@ -4,5 +4,7 @@ + + diff --git a/libs/dom4j-1.6.1.jar b/libs/dom4j-1.6.1.jar new file mode 100644 index 0000000..c8c4dbb Binary files /dev/null and b/libs/dom4j-1.6.1.jar differ diff --git a/libs/wsdl4j-1.6.2.jar b/libs/wsdl4j-1.6.2.jar new file mode 100644 index 0000000..b9ffc36 Binary files /dev/null and b/libs/wsdl4j-1.6.2.jar differ diff --git a/src/zutil/MultiPrintStream.java b/src/zutil/MultiPrintStream.java index c9d9d3f..696ec9d 100644 --- a/src/zutil/MultiPrintStream.java +++ b/src/zutil/MultiPrintStream.java @@ -94,6 +94,22 @@ public class MultiPrintStream extends PrintStream { streams.remove(p); } + /** + * writes to all the PrintStreams + */ + public void write(int b) { + for(int i=0; i implements Queue{ - + // GO TO KNOW = SELECT LAST_INSERT_ID() as pos_id private MySQLConnection db; private String table; diff --git a/src/zutil/network/http/HttpServer.java b/src/zutil/network/http/HttpServer.java index 1f21838..f3c6463 100644 --- a/src/zutil/network/http/HttpServer.java +++ b/src/zutil/network/http/HttpServer.java @@ -16,6 +16,7 @@ import javax.net.ssl.SSLServerSocketFactory; import zutil.MultiPrintStream; + /** * A simple web server that handles both cookies and * sessions for all the clients diff --git a/src/zutil/network/http/soap/SOAPClientException.java b/src/zutil/network/http/soap/SOAPClientException.java new file mode 100644 index 0000000..9002090 --- /dev/null +++ b/src/zutil/network/http/soap/SOAPClientException.java @@ -0,0 +1,15 @@ +package zutil.network.http.soap; + +/** + * This generates an client fault message + * when used with SOAPHttpPage + * + * @author Ziver + */ +public class SOAPClientException extends Exception{ + private static final long serialVersionUID = 1L; + + public SOAPClientException(String string) { + super(string); + } +} diff --git a/src/zutil/network/http/soap/SOAPHttpPage.java b/src/zutil/network/http/soap/SOAPHttpPage.java new file mode 100644 index 0000000..1335f2c --- /dev/null +++ b/src/zutil/network/http/soap/SOAPHttpPage.java @@ -0,0 +1,737 @@ +package zutil.network.http.soap; + +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.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 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 zutil.MultiPrintStream; +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 com.ibm.wsdl.extensions.PopulatedExtensionRegistry; +import com.ibm.wsdl.extensions.soap.SOAPConstants; +import com.sun.org.apache.xerces.internal.dom.DocumentImpl; + + +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; + + 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; + methods = new HashMap(); + + for(Method m : interf.getClass().getDeclaredMethods()){ + // check for public methods + if(m.getModifiers() == Modifier.PUBLIC && + !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.sendHeader("Content-Type: text/xml"); + + if(request.containsKey("wsdl")){ + out.println(""); + out.flush(); + 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{ + Document document = soap( request.get("") ); + + OutputFormat format = OutputFormat.createPrettyPrint(); + XMLWriter writer = new XMLWriter( out, format ); + writer.write( document ); + } + } catch (Exception e) { + e.printStackTrace(MultiPrintStream.out); + } + + } + + public Document soap(String xml){ + 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 body = envelope.addElement( "soap:Body" ); + try{ + Element request = getXMLRoot(xml); + Object obj = interf.getClass().newInstance(); + // Header + if( request.element("Header") != null){ + Element header = envelope.addElement( "soap:Header" ); + 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){ + 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); + return null; + } + + /** + * Invokes a specified method + * + * @param m is the function + * @param params a vector with arguments + * @throws Throwable + */ + public 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 + */ + public Element getXMLRoot(String xml) throws Exception { + if(xml != null && !xml.isEmpty()){ + Document document = DocumentHelper.parseText(xml); + return document.getRootElement(); + } + return null; + } + + /** + * Generates an WSDL document for the class + * + * @throws WSDLException + */ + private void generateWSDL() throws WSDLException{ + ArrayList> types = new ArrayList>(); + + PopulatedExtensionRegistry extReg = new PopulatedExtensionRegistry(); + WSDLFactory factory = WSDLFactory.newInstance(); + 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"; + 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(( SOAPObject.class.isAssignableFrom(cTmp) )){ + // is is an array? + if(m.method.getReturnType().isArray()){ + part.setTypeName(new QName(soap, "Array")); + part.setExtensionAttribute( + new QName(soap, "arrayType"), + new QName(td, m.method.getReturnType().getSimpleName().toLowerCase())); + } + else{ // its an Object + part.setTypeName(new QName(td, + m.method.getReturnType().getSimpleName().toLowerCase())); + } + // add to type generation list + if(!types.contains(cTmp)) + types.add(cTmp); + } + else{ + // is is an array? + if(m.method.getReturnType().isArray()){ + part.setTypeName(new QName(soap, "Array")); + part.setExtensionAttribute( + new QName(soap, "arrayType"), + new QName(xsd, m.method.getReturnType().getSimpleName().toLowerCase())); + } + else{ // its an Object + part.setTypeName(new QName(xsd, + m.method.getReturnType().getSimpleName().toLowerCase())); + } + } + + 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.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.addNamespace("targetNamespace", url+"?type"); + definitions.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema"); + definitions.addNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/"); + + Element typeE = definitions.addElement("wsdl:types"); + Element schema = typeE.addElement("xsd:schema"); + + for(int n=0; n c = types.get(n); + Element type = schema.addElement("xsd:complexType"); + type.addAttribute("name", c.getSimpleName().toLowerCase()); + + 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:"+cTmp.getSimpleName().toLowerCase()); + if(!types.contains(cTmp)) + types.add(cTmp); + } + else + element.addAttribute("type", "xsd:"+cTmp.getSimpleName().toLowerCase()); + // 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; + } + + //******************************************************************************************* + //**************************** TEST ********************************************************* + + public static void main(String[] args){ + try { + new SOAPHttpPage("http://test.se:8080/", new Test()).test(); + } catch (WSDLException e) { + e.printStackTrace(); + } + } + + private static class TestObject2 implements SOAPObject{ + public String lol = "lol11"; + public String lol2 = "lol22"; + } + + private static class TestObject implements SOAPObject{ + @SOAPFieldName(value="lolz", optional=true) + public String lol = "lol1"; + @SOAPFieldName("lolx") + public String lol2 = "lol2"; + public TestObject2 l = new TestObject2(); + } + + private static class Test implements SOAPInterface{ + public Test(){} + + @SOAPHeader() + @WSDLDocumentation("hello") + public void pubZ( + @SOAPParamName(value="olle", optional=true) int lol) throws Exception{ + //System.out.println("Param: "+lol); + throw new Exception("Ziver is the fizle"); + } + + @SOAPReturnName("param") + @WSDLParamDocumentation("null is the shizzle") + public String[][] pubA ( + @SOAPParamName("Ztring") String lol) throws Exception{ + //System.out.println("ParamZ: "+lol); + return new String[][]{{"test","test2"},{"test3","test4"}}; + } + + @SOAPReturnName("zivarray") + @WSDLParamDocumentation("null is the shizzle") + public TestObject[] pubX ( + @SOAPParamName("Ztring") String lol) throws Exception{ + return new TestObject[]{new TestObject(), new TestObject()}; + } + + @SOAPDisabled() + public void privaZ(){ } + protected void protZ(){ } + } + + public void test(){ + // Response + try { + Document document = soap( + "" + + "\n" + + " \n" + + //" \n" + + //" IBM\n" + + //" \n" + + //" \n" + + //" 66\n" + + //" \n" + + " \n" + + " IBM\n" + + " \n" + + " \n" + + ""); + System.out.println(); + + OutputFormat format = OutputFormat.createPrettyPrint(); + XMLWriter writer = new XMLWriter( System.out, format ); + writer.write( document ); + + System.out.println(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/zutil/network/http/soap/SOAPInterface.java b/src/zutil/network/http/soap/SOAPInterface.java new file mode 100644 index 0000000..65f3b9f --- /dev/null +++ b/src/zutil/network/http/soap/SOAPInterface.java @@ -0,0 +1,102 @@ +package zutil.network.http.soap; + +import java.lang.annotation.*; + +/** + * + * Specifies SOAP parameters names an other things. + * Example: + *
+ *	private static class Test implements SOAPInterface{
+ *		public Test(){}
+ *	
+ *		@SOAPHeader()
+ *		@WSDLDocumentation("blabla")
+ *		@WSDLParamDocumentation("olle = an variable?")
+ *		public void pubZ( 
+ *				@SOAPParamName("olle") int lol) 
+ *				throws Exception{ 
+ *			....
+ *		}
+ *	
+ *		@SOAPReturnName("param")
+ *		public String pubA( 
+ *				@SOAPParamName(value="lol", optional=true) String lol) 
+ *				throws Exception{ 
+ *			....
+ *		}
+ *	
+ *		@SOAPDisabled()
+ *		public void privaZ(....){ 
+ *			...
+ *		}		
+ *	}
+ * 
+ * 
+ * @author Ziver + */ +public interface SOAPInterface { + /** + * Annotation that assigns a name to an parameters + * in an method. + * + * @author Ziver + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.PARAMETER) + public @interface SOAPParamName { + String value(); + boolean optional() default false; + } + + /** + * Annotation that assigns a name to the return value + * in an method. + * + * @author Ziver + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface SOAPReturnName { + String value(); + } + + /** + * Disables SOAP publication of the given method + * + * @author Ziver + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface SOAPDisabled { } + + /** + * Method comments for the WSDL. + * These comments are put in the operation part of the WSDL + * + * @author Ziver + */ + @Retention(RetentionPolicy.RUNTIME) + public @interface WSDLDocumentation{ + String value(); + } + /** + * Parameter comments for the WSDL. + * These comments are put in the message part of the WSDL + * + * @author Ziver + */ + @Retention(RetentionPolicy.RUNTIME) + public @interface WSDLParamDocumentation{ + String value(); + } + + /** + * This method will be used in the header of the soap. + * + * @author Ziver + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface SOAPHeader { } +} diff --git a/src/zutil/network/http/soap/SOAPObject.java b/src/zutil/network/http/soap/SOAPObject.java new file mode 100644 index 0000000..e2ef5a1 --- /dev/null +++ b/src/zutil/network/http/soap/SOAPObject.java @@ -0,0 +1,43 @@ +package zutil.network.http.soap; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This class is used as an return Object for SOAP. + * If an class implements this interface then it can return + * multiple values through the SOAPInterface. Example: + *
+ * 	private static class TestObject implements SOAPObject{
+ *		@SOAPFieldName("name")
+ *		public String name;
+ *		@SOAPFieldName("lastname")
+ *		public String lastname;
+ *
+ *		public TestObject(String n, String l){
+ *			name = n;
+ *			lastname = l;
+ *		}
+ *	}
+ * 
+ * + * @author Ziver + * + */ +public interface SOAPObject{ + /** + * Specifies the SOAP name of an field. + * The fields that are available for SOAP should + * be declared public. + * + * @author Ziver + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface SOAPFieldName { + String value(); + boolean optional() default false; + } +} \ No newline at end of file