From 404690f2c67a124aa34b91837ca3efe76425d48e Mon Sep 17 00:00:00 2001 From: "fabio.simeoni" Date: Mon, 7 Jan 2013 14:08:13 +0000 Subject: [PATCH] git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/branches/common/common-gcore-stubs/1.0@67134 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 42 +++ .project | 23 ++ distro/INSTALL | 1 + distro/LICENSE | 6 + distro/MAINTAINERS | 1 + distro/README | 38 ++ distro/changelog.xml | 5 + distro/descriptor.xml | 38 ++ distro/profile.xml | 26 ++ distro/svnpath.txt | 1 + pom.xml | 208 +++++++++++ .../clients/stubs/jaxws/ExceptionProxy.java | 108 ++++++ .../stubs/jaxws/GCoreEndpointReference.java | 84 +++++ .../stubs/jaxws/GCoreJAXWSHandler.java | 127 +++++++ .../clients/stubs/jaxws/GCoreService.java | 93 +++++ .../stubs/jaxws/GCoreServiceBuilder.java | 61 +++ .../clients/stubs/jaxws/GCoreServiceDSL.java | 64 ++++ .../clients/stubs/jaxws/JAXWSUtils.java | 66 ++++ .../common/clients/stubs/jaxws/StubCache.java | 107 ++++++ .../clients/stubs/jaxws/StubFactory.java | 189 ++++++++++ .../clients/stubs/jaxws/StubFactoryDSL.java | 39 ++ src/test/java/org/acme/StubTest.java | 258 +++++++++++++ .../java/org/acme/jaxws/stubs/BarInput.java | 15 + .../java/org/acme/jaxws/stubs/BarOutput.java | 13 + .../org/acme/jaxws/stubs/FooException.java | 13 + .../org/acme/jaxws/stubs/StatefulStub.java | 33 ++ .../org/acme/jaxws/stubs/StatelessStub.java | 82 ++++ src/test/java/org/acme/jaxws/stubs/Types.java | 61 +++ .../org/acme/jaxws/stubs/VoidWrapper.java | 8 + src/test/java/org/acme/service/Factory.java | 39 ++ src/test/java/org/acme/service/Home.java | 11 + src/test/java/org/acme/service/Resource.java | 50 +++ .../java/org/acme/service/ServiceContext.java | 25 ++ src/test/java/org/acme/service/Stateful.java | 35 ++ .../org/acme/service/StatefulContext.java | 34 ++ src/test/java/org/acme/service/Stateless.java | 158 ++++++++ .../org/acme/service/StatelessContext.java | 28 ++ src/test/java/org/acme/service/Utils.java | 17 + src/test/resources/log4j.properties | 10 + .../test-service/etc/deploy-jndi-config.xml | 31 ++ .../test-service/etc/deploy-server.wsdd | 32 ++ .../resources/test-service/etc/profile.xml | 39 ++ .../resources/test-service/wsdl/Stateful.wsdl | 47 +++ .../test-service/wsdl/Stateless.wsdl | 352 ++++++++++++++++++ 44 files changed, 2718 insertions(+) create mode 100644 .classpath create mode 100644 .project create mode 100644 distro/INSTALL create mode 100644 distro/LICENSE create mode 100644 distro/MAINTAINERS create mode 100644 distro/README create mode 100644 distro/changelog.xml create mode 100644 distro/descriptor.xml create mode 100644 distro/profile.xml create mode 100644 distro/svnpath.txt create mode 100644 pom.xml create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/ExceptionProxy.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreEndpointReference.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreJAXWSHandler.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreService.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceBuilder.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceDSL.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/JAXWSUtils.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/StubCache.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactory.java create mode 100644 src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactoryDSL.java create mode 100644 src/test/java/org/acme/StubTest.java create mode 100644 src/test/java/org/acme/jaxws/stubs/BarInput.java create mode 100644 src/test/java/org/acme/jaxws/stubs/BarOutput.java create mode 100644 src/test/java/org/acme/jaxws/stubs/FooException.java create mode 100644 src/test/java/org/acme/jaxws/stubs/StatefulStub.java create mode 100644 src/test/java/org/acme/jaxws/stubs/StatelessStub.java create mode 100644 src/test/java/org/acme/jaxws/stubs/Types.java create mode 100644 src/test/java/org/acme/jaxws/stubs/VoidWrapper.java create mode 100644 src/test/java/org/acme/service/Factory.java create mode 100644 src/test/java/org/acme/service/Home.java create mode 100644 src/test/java/org/acme/service/Resource.java create mode 100644 src/test/java/org/acme/service/ServiceContext.java create mode 100644 src/test/java/org/acme/service/Stateful.java create mode 100644 src/test/java/org/acme/service/StatefulContext.java create mode 100644 src/test/java/org/acme/service/Stateless.java create mode 100644 src/test/java/org/acme/service/StatelessContext.java create mode 100644 src/test/java/org/acme/service/Utils.java create mode 100644 src/test/resources/log4j.properties create mode 100755 src/test/resources/test-service/etc/deploy-jndi-config.xml create mode 100644 src/test/resources/test-service/etc/deploy-server.wsdd create mode 100644 src/test/resources/test-service/etc/profile.xml create mode 100644 src/test/resources/test-service/wsdl/Stateful.wsdl create mode 100644 src/test/resources/test-service/wsdl/Stateless.wsdl diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..ef1ba90 --- /dev/null +++ b/.classpath @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..8e055d5 --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + common-gcore-stubs + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/distro/INSTALL b/distro/INSTALL new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/distro/INSTALL @@ -0,0 +1 @@ + diff --git a/distro/LICENSE b/distro/LICENSE new file mode 100644 index 0000000..630ba97 --- /dev/null +++ b/distro/LICENSE @@ -0,0 +1,6 @@ +gCube System - License +------------------------------------------------------------ + +The gCube/gCore software is licensed as Free Open Source software conveying to the EUPL (http://ec.europa.eu/idabc/eupl). +The software and documentation is provided by its authors/distributors "as is" and no expressed or +implied warranty is given for its use, quality or fitness for a particular case. diff --git a/distro/MAINTAINERS b/distro/MAINTAINERS new file mode 100644 index 0000000..5e8aa8f --- /dev/null +++ b/distro/MAINTAINERS @@ -0,0 +1 @@ +* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy \ No newline at end of file diff --git a/distro/README b/distro/README new file mode 100644 index 0000000..feab5a2 --- /dev/null +++ b/distro/README @@ -0,0 +1,38 @@ +The gCube System - ${name} +---------------------- + +This work has been partially supported by the following European projects: DILIGENT (FP6-2003-IST-2), D4Science (FP7-INFRA-2007-1.2.2), +D4Science-II (FP7-INFRA-2008-1.2.2), iMarine (FP7-INFRASTRUCTURES-2011-2), and EUBrazilOpenBio (FP7-ICT-2011-EU-Brazil). + +Authors +------- + +* Fabio Simeoni (fabio.simeoni@fao.org), FAO of the UN, Italy + +Version and Release Date +------------------------ +${version} + +Description +----------- +${description} + +Download information +-------------------- + +Source code is available from SVN: +${scm.url} + +Binaries can be downloaded from: + + +Documentation +------------- +Documentation is available on-line from the Projects Documentation Wiki: +https://gcube.wiki.gcube-system.org/gcube/index.php + + +Licensing +--------- + +This software is licensed under the terms you may find in the file named "LICENSE" in this directory. diff --git a/distro/changelog.xml b/distro/changelog.xml new file mode 100644 index 0000000..cec4f71 --- /dev/null +++ b/distro/changelog.xml @@ -0,0 +1,5 @@ + + + First Release + + \ No newline at end of file diff --git a/distro/descriptor.xml b/distro/descriptor.xml new file mode 100644 index 0000000..06c416f --- /dev/null +++ b/distro/descriptor.xml @@ -0,0 +1,38 @@ + + servicearchive + + tar.gz + + / + + + ${distroDirectory} + / + true + + README + LICENSE + INSTALL + MAINTAINERS + changelog.xml + profile.xml + + 755 + true + + + + + target/${build.finalName}.jar + /${artifactId} + + + ${distroDirectory}/svnpath.txt + /${artifactId} + true + + + \ No newline at end of file diff --git a/distro/profile.xml b/distro/profile.xml new file mode 100644 index 0000000..91c49e4 --- /dev/null +++ b/distro/profile.xml @@ -0,0 +1,26 @@ + + + + Service + + ${description} + Common + ${artifactId} + 1.0.0 + + + ${artifactId} + ${version} + + ${groupId} + ${artifactId} + ${version} + + + ${build.finalName}.jar + + + + + + diff --git a/distro/svnpath.txt b/distro/svnpath.txt new file mode 100644 index 0000000..f416f9d --- /dev/null +++ b/distro/svnpath.txt @@ -0,0 +1 @@ +${scm.url} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..da5018c --- /dev/null +++ b/pom.xml @@ -0,0 +1,208 @@ + + 4.0.0 + + org.gcube.tools + maven-parent + 1.0.0 + + org.gcube.core + common-gcore-stubs + 1.0.0-SNAPSHOT + GCore Stubs + JAXWS Stub Support for gCore Service + + + scm:svn:http://svn.d4science.research-infrastructures.eu/gcube/trunk/Common/${project.artifactId} + scm:svn:https://svn.d4science.research-infrastructures.eu/gcube/trunk/Common/${project.artifactId} + http://svn.d4science.research-infrastructures.eu/gcube/trunk/Common/${project.artifactId} + + + + distro + + + + + + org.slf4j + slf4j-api + 1.6.4 + + + + org.gcube.core + common-scope + [1.1.0-SNAPSHOT,2.0.0-SNAPSHOT) + + + + + + + + org.gcube.core + gcf + [1.5.0-SNAPSHOT,2.0.0-SNAPSHOT) + provided + + + + + org.slf4j + slf4j-simple + 1.6.4 + test + + + + + org.gcube.tools + my-container + 1.0.0 + test + + + + + org.gcube.tools + my-container + 1.0.0 + tar.gz + distro + test + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-profile + install + + copy-resources + + + target + + + ${distroDirectory} + true + + profile.xml + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + ${distroDirectory}/descriptor.xml + + + + + servicearchive + install + + single + + + + + + + + + + org.gcube.tools + maven-service-plugin + 1.0.0 + + org.acme.sample + src/test/resources/test-service/wsdl + src/test/resources/test-service/etc + + + Stateless + http://acme.org + + + Stateful + http://acme.org + + + + + + generate-stubs + generate-test-resources + + stub-gen + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + add-source + generate-test-sources + + add-test-source + + + + ${project.basedir}/target/generated-sources/stubs + + + + + + + + + maven-dependency-plugin + + + install-my-container + generate-test-resources + + my-container + tar.gz + false + ${project.basedir} + ${project.basedir} + + + unpack-dependencies + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/ExceptionProxy.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/ExceptionProxy.java new file mode 100644 index 0000000..a5ce0a9 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/ExceptionProxy.java @@ -0,0 +1,108 @@ +/** + * + */ +package org.gcube.common.clients.stubs.jaxws; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * @author fabio + * + */ +@XmlRootElement(namespace="http://gcube-system.org",name="stacktrace") +public class ExceptionProxy { + + + static { + try { + context=JAXBContext.newInstance(ExceptionProxy.class); + } + catch(Throwable t) { + throw new AssertionError(t); + } + } + + + @XmlType(name="e") + static class StackTraceElementProxy { + + @XmlAttribute public String cn; + @XmlAttribute public String mn; + @XmlAttribute public String fn; + @XmlAttribute public int ln; + + } + + private static JAXBContext context; + private static final DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); + + + public static ExceptionProxy newInstance(Element e) throws Exception { + return (ExceptionProxy) context.createUnmarshaller().unmarshal(e); + } + + public static ExceptionProxy newInstance(Throwable t) { + + ExceptionProxy p = new ExceptionProxy(); + p.msg=t.getMessage(); + p.name=t.getClass().getCanonicalName(); + + for (StackTraceElement e : t.getStackTrace()) { + StackTraceElementProxy ep = new StackTraceElementProxy(); + ep.cn= e.getClassName(); + ep.mn=e.getMethodName(); + ep.fn=e.getFileName(); + ep.ln=e.getLineNumber(); + p.el.add(ep); + } + + if (t.getCause()!=null) + p.c= newInstance(t.getCause()); + + return p; + + } + + + @XmlAttribute public String name; + @XmlAttribute public String msg; + @XmlElement public List el = new ArrayList(); + @XmlElement public ExceptionProxy c; + + + public Throwable toThrowable() { + String msg = "remote cause: ("+(this.msg==null?name:this.msg)+")"; + Throwable t = c==null? new Throwable(msg): + new Throwable(msg,c.toThrowable()); + + List elements = new ArrayList(); + + for (StackTraceElementProxy ep : el) + elements.add(new StackTraceElement(ep.cn, ep.mn, ep.fn, ep.ln)); + t.setStackTrace(elements.toArray(new StackTraceElement[0])); + + + return t; + } + + + public Element toElement() throws Exception { + Document d = domFactory.newDocumentBuilder().newDocument(); + context.createMarshaller().marshal(this,d); + return d.getDocumentElement(); + } + + +} + diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreEndpointReference.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreEndpointReference.java new file mode 100644 index 0000000..9f121a5 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreEndpointReference.java @@ -0,0 +1,84 @@ +package org.gcube.common.clients.stubs.jaxws; + +import java.io.StringReader; +import java.io.StringWriter; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.ws.EndpointReference; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +/** + * Used internally by {@link StubFactory} to bridge {@link EndpointReference}s to gCore instances which comply with the + * older Member Specification of WS-Addressing (e.g. as returned by a gCore factory service) with + * {@link EndpointReference}s to same instances that comply with W3C's specification of WS-Addressing. + *

+ * Since JAX-WS does not support Member Addressing directly, nor does the RI embedded in the JDK, reference to gCore + * instances that are produced by gCore services must be manually transformed into a standard form. This requires + * extracting endpoint address and resource key from the references and use them to build a standard reference. + * + * @author Fabio Simeoni + * + */ +class GCoreEndpointReference { + + private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + private static final String addressLocalName = "Address"; + private static final String keyLocalName = "ResourceKey"; + + String address; + Element key; + + static { + factory.setNamespaceAware(true); + } + + GCoreEndpointReference(EndpointReference reference) { + this(serialise(reference)); + } + + GCoreEndpointReference(String reference) { + + try { + + Document document = factory.newDocumentBuilder().parse(new InputSource(new StringReader(reference))); + + NodeList addresses = document.getElementsByTagNameNS("*", addressLocalName); + + if (addresses.getLength() == 0) + throw new RuntimeException("reference does not contain an address"); + + address = addresses.item(0).getTextContent(); + + NodeList keys = document.getElementsByTagNameNS("*", keyLocalName); + + if (keys.getLength() >1) + throw new RuntimeException("reference contains " + keys.getLength() + " resource key(s)"); + + if (keys.getLength()==1) + key = (Element) keys.item(0); + + } catch (Exception e) { + throw new IllegalArgumentException("reference is not a gCore reference", e); + } + + } + + @Override + public String toString() { + return address + ":" + key.getTextContent(); + } + + // helper + private static String serialise(EndpointReference reference) { + StringWriter writer = new StringWriter(); + reference.writeTo(new StreamResult(writer)); + return writer.toString(); + } + +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreJAXWSHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreJAXWSHandler.java new file mode 100644 index 0000000..4c55e76 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreJAXWSHandler.java @@ -0,0 +1,127 @@ +package org.gcube.common.clients.stubs.jaxws; + +import java.util.Iterator; +import java.util.Set; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPHeader; +import javax.xml.soap.SOAPHeaderElement; +import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPHandler; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.scope.api.ScopeProvider; + +/** + * A {@link SOAPHandler} that adds gCube headers to outgoing calls. + * + * @author Fabio Simeoni + * + */ +public class GCoreJAXWSHandler implements SOAPHandler { + + /** Namespace of scope-related headers */ + public static final String SCOPE_NS = "http://gcube-system.org/namespaces/scope"; + + /** Name of the scope call header. */ + public static final String SCOPE_HEADER_NAME = "scope"; + public static final QName SCOPE_QNAME = new QName(SCOPE_NS,SCOPE_HEADER_NAME); + + /** Name of the service class call header. */ + public static final String SERVICECLASS_HEADER_NAME = "serviceClass"; + public static final QName SERVICECLASS_QNAME = new QName(SCOPE_NS,SERVICECLASS_HEADER_NAME); + + /** Name of the service name call header. */ + public static final String SERVICENAME_HEADER_NAME = "serviceName"; + public static final QName SERVICENAME_QNAME = new QName(SCOPE_NS,SERVICENAME_HEADER_NAME); + + + /** Name of the scope call header. */ + public static final String CALLER_HEADER_NAME = "caller"; + /** Namespace of scope-related headers */ + public static final String CALLER_NS = "http://gcube-system.org/namespaces/caller"; + + public static final QName CALLER_QNAME = new QName(CALLER_NS,CALLER_HEADER_NAME); + + private final GCoreService target; + + GCoreJAXWSHandler(GCoreService target) { + this.target=target; + } + + public boolean handleMessage(SOAPMessageContext context) { + + Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); + + if (outbound) + try { + + SOAPHeader header = context.getMessage().getSOAPPart().getEnvelope().getHeader(); + + if (header == null) + header = context.getMessage().getSOAPPart().getEnvelope().addHeader(); + + + addCurrentScope(header); + addTargetServiceCoordinates(header); + addClientIdentity(header); + + correctWSAddressingHeader(header); + + } catch (Exception e) { + throw new RuntimeException("cannot configure outgoing message", e); + } + + return true; + }; + + public Set getHeaders() { + return null; + } + + public boolean handleFault(SOAPMessageContext context) { + return true; + } + + public void close(MessageContext context) {} + + + //helper + private void addClientIdentity(SOAPHeader header) throws Exception { + addHeader(header,CALLER_QNAME, target.clientId()); + } + + //helper + private void addTargetServiceCoordinates(SOAPHeader header) throws Exception { + addHeader(header,SERVICECLASS_QNAME, target.gcubeClass()); + addHeader(header,SERVICENAME_QNAME, target.gcubeName()); + } + + //helper + private void addCurrentScope(SOAPHeader header) throws Exception { + String scope = ScopeProvider.instance.get(); + if (scope==null) + throw new IllegalStateException("no scope is defined for this call"); + addHeader(header,SCOPE_QNAME, scope); + } + + //helper: adapts ws-addressing headers to member submission's. brutal but there is no support for member submission in + //jdk 1.6 + private void correctWSAddressingHeader(SOAPHeader header) throws Exception { + + Iterator it = header.examineAllHeaderElements(); + while (it.hasNext()) { + SOAPHeaderElement e = (SOAPHeaderElement) it.next(); + if (e.getElementQName().getNamespaceURI().equals("http://www.w3.org/2005/08/addressing")) { + e.detachNode(); + addHeader(header,new QName("http://schemas.xmlsoap.org/ws/2004/03/addressing",e.getElementQName().getLocalPart()), e.getTextContent()); + } + + } + } + + // helper + private void addHeader(SOAPHeader header,QName name, String value) throws Exception { + header.addHeaderElement(name).addTextNode(value); + } +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreService.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreService.java new file mode 100644 index 0000000..f676775 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreService.java @@ -0,0 +1,93 @@ +package org.gcube.common.clients.stubs.jaxws; + +import java.net.InetAddress; + +import javax.xml.namespace.QName; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Groups information required to generate a stub for a gCore service. + * + * @author Fabio Simeoni + * + * @param the interface of the stub + * + * @see StubFactory + */ +public final class GCoreService { + + Logger logger = LoggerFactory.getLogger(GCoreService.class); + + private final String serviceClass; + private final String serviceName; + private String identity; + private final QName name; + private final Class type; + + /** + * Creates an instance for a given gCore service. + * + * @param gcubeClass the gCube class of the service, as specified in its gCube profile + * @param gcubeName the gCUbe name of the service, as specified in its gCube profile + * @param qname the qualified name of the service, as specified in its WSDL + * @param type the interface of the service stub + */ + public GCoreService(String gcubeClass, String gcubeName, QName qname, Class type) { + + this.serviceClass=gcubeClass; + this.serviceName=gcubeName; + this.name=qname; + this.type=type; + + try { + this.identity=InetAddress.getLocalHost().getHostAddress(); + } + catch(Exception e) { + logger.warn("cannot determine local address as a client identity",e); + this.identity="uknown"; + } + } + + /** + * Returns the gCube class of the service,, as specified in the service profile. + * @return the name + */ + public String gcubeClass() { + return serviceClass; + } + + /** + * Returns the gCube name of the service, as specified in the service profile. + * @return the name + */ + public String gcubeName() { + return serviceName; + } + + /** + * Returns the identity of the service client. + * @return the client identity + */ + public String clientId() { + return identity; + } + + /** + * Returns the name of the service, as specified in the service WSDL. + * @return the class. + */ + public QName qName() { + return name; + } + + /** + * Returns the interface of the service stub. + * @return the interface + */ + public Class type() { + return type; + } + +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceBuilder.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceBuilder.java new file mode 100644 index 0000000..dccab79 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceBuilder.java @@ -0,0 +1,61 @@ +package org.gcube.common.clients.stubs.jaxws; + +import static org.gcube.common.clients.stubs.jaxws.JAXWSUtils.*; + +import javax.xml.namespace.QName; + +import org.gcube.common.clients.stubs.jaxws.GCoreServiceDSL.CoordinateClause; +import org.gcube.common.clients.stubs.jaxws.GCoreServiceDSL.NameClause; +import org.gcube.common.clients.stubs.jaxws.GCoreServiceDSL.StubClause; + +/** + * Builds {@link GCoreService} instances. + * + * @author Fabio Simeoni + * + */ +public class GCoreServiceBuilder implements NameClause, CoordinateClause, StubClause { + + private QName name; + private String gcubeclass; + private String gcubename; + + /** + * Starts the bulding process for a {@link GCoreService}. + * @return the service + */ + public static NameClause service() { + return new GCoreServiceBuilder(); + } + + @Override + public CoordinateClause withName(QName name) { + + notNull("service name", name); + + this.name=name; + + return this; + } + + @Override + public StubClause coordinates(String gcubeClass, String gcubeName) { + + notNull("service class", gcubeClass); + notNull("service name", gcubeName); + + this.gcubeclass=gcubeClass; + this.gcubename=gcubeName; + + return this; + } + + @Override + public GCoreService andInterface(Class type) { + + notNull("service interface", type); + + return new GCoreService(gcubeclass, gcubename, name, type); + } + +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceDSL.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceDSL.java new file mode 100644 index 0000000..a3b37f5 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreServiceDSL.java @@ -0,0 +1,64 @@ +package org.gcube.common.clients.stubs.jaxws; + +import javax.xml.namespace.QName; + +/** + * The clauses of a simple DSL to build {@link GCoreService}. + * + * @author Fabio Simeoni + * + */ +public interface GCoreServiceDSL { + + /** + * The clause that sets the name of the target service. + * + * @author Fabio Simeoni + * + */ + static interface NameClause { + + /** + * Sets the qualified name of the target service. + * + * @param name the qualified name of the target service + * @return the next clause + */ + CoordinateClause withName(QName name); + + } + + /** + * The clause that sets the gCube coordinates of the target service. + * + * @author Fabio Simeoni + * + */ + static interface CoordinateClause { + + /** + * Sets the gCube coordinates of the target service + * @param gcubeClass the gCube class of the target service + * @param gcubeName the gCube name of the target service + * @return + */ + StubClause coordinates(String gcubeClass, String gcubeName); + + } + + /** + * The clause that sets the stub interface of the target service. + * + * @author Fabio Simeoni + * + */ + static interface StubClause { + + /** + * Sets the stub interface of the target service. + * @param type the interface + * @return the {@link GCoreService} that described the target service. + */ + GCoreService andInterface(Class type); + } +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/JAXWSUtils.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/JAXWSUtils.java new file mode 100644 index 0000000..6ef89ca --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/JAXWSUtils.java @@ -0,0 +1,66 @@ +package org.gcube.common.clients.stubs.jaxws; + +import java.util.Iterator; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.soap.DetailEntry; +import javax.xml.soap.SOAPFault; +import javax.xml.ws.soap.SOAPFaultException; + +/** + * Library-wide utilities. + * + * @author Fabio Simeoni + * + */ +public class JAXWSUtils { + + @XmlRootElement + public static class Empty{} + + public static final Empty empty = new Empty(); + + /** + * Returns the remote cause of a {@link SOAPFaultException} as a {@link Throwable}. + * + * @param e the exception + * @return a {@link Throwable} deserialised from the stacktrace found in the {@link SOAPFault} inside the exception, + * or the input exception itself if the stacktrace cannot be found. + */ + public static Throwable remoteCause(SOAPFaultException e) { + + // if we cannt do better, we throw this + Throwable throwable = e; + + SOAPFault faultBean = e.getFault(); + + // if a serialised stacktrace is available, parse it and convert it into a throwable + Iterator details = faultBean.getDetail().getDetailEntries(); + while (details.hasNext()) { + + DetailEntry detail = (DetailEntry) details.next(); + String ns = detail.getNamespaceURI(); + String local = detail.getLocalName(); + + boolean match = ns != null && ns.equals("http://gcube-system.org") && local != null + && local.equals("stacktrace"); + + if (match) + try { + ExceptionProxy proxy = ExceptionProxy.newInstance(detail); + throwable = proxy.toThrowable(); + } catch (Throwable t) { + throwable = new Exception("could not parse remote fault", t); + } + } + + return throwable; + } + + + static void notNull(String message,Object o) { + if (o==null) + throw new IllegalArgumentException(o+" cannot be null"); + } + +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/StubCache.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubCache.java new file mode 100644 index 0000000..84df48a --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubCache.java @@ -0,0 +1,107 @@ +package org.gcube.common.clients.stubs.jaxws; + +import java.util.LinkedHashMap; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Privately used by a {@link StubFactory}, caches {@link Service} instances for services with given names. + *

+ * The cache is LRU and bounded at a maximum size of {@link LRUCache#max}. It is also thread-safe, though synchronisation + * occurs on a per-key basis. + * + * @author Fabio Simeoni + * @see StubFactory + */ +class StubCache { + + private static final Logger log = LoggerFactory.getLogger(StubCache.class); + + private volatile LRUCache cache = new LRUCache(); + + //holds key locks for LRU map + private ConcurrentHashMap nameLocks = new ConcurrentHashMap(); + + Service get(QName name,Callable task) { + + //obtain a lock for current key + Lock nameLock = lockFor(name); + + nameLock.lock(); + + try { + + Service service = cache.get(name); + + if (service==null) + try { + service= task.call(); + log.trace("caching stub for "+name); + cache.put(name,service); + } + catch(Exception e) { + throw new RuntimeException("could not build service",e); + } + else { + log.trace("using cached stub for "+name); + } + + return service; + } + finally { + nameLock.unlock(); + } + } + + //helper + private Lock lockFor(QName name) { + + Lock nameLock = nameLocks.get(name); + + //no need to create a new lock a priori + if (nameLock==null) { + + Lock newLock = new ReentrantLock(); + + //get name lock, creating it if it doesn't exist + //this is where we first synchronise: second-come thread waits for new one to have put a lock + //then it gets that same shared lock + nameLock = nameLocks.putIfAbsent(name,newLock); + + nameLock = nameLock == null? newLock : nameLock; + } + + return nameLock; + } + + private class LRUCache extends LinkedHashMap { + + private static final long serialVersionUID = 1L; + + public static final int max = 50; + + public LRUCache() { + //use defaults, but indicate accessor-order as 3rd parameter (rather than default insertion order) + super(16,.75f,true); + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + if (size()>=max) { + nameLocks.remove(eldest.getKey()); + return true; + } + + return false; + } + } + +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactory.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactory.java new file mode 100644 index 0000000..1cbf1ab --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactory.java @@ -0,0 +1,189 @@ +package org.gcube.common.clients.stubs.jaxws; + +import static org.gcube.common.clients.stubs.jaxws.JAXWSUtils.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.List; +import java.util.concurrent.Callable; + +import javax.xml.namespace.QName; +import javax.xml.ws.Binding; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.EndpointReference; +import javax.xml.ws.Service; +import javax.xml.ws.handler.Handler; +import javax.xml.ws.soap.AddressingFeature; +import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder; + +import org.gcube.common.clients.stubs.jaxws.StubFactoryDSL.AtClause; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Generates JAXWS stubs for endpoints or instances of gCore service at given addresses. + * + *

+ * Factories are instantiated with descriptions of target services (cf. {@link GCoreService}) and use them to perform the + * following tasks: + * + *

    + *
  • interact with the JAXWS APIs to dynamically implement stub interfaces. + *
  • resolve service WSDLs to enable the interactions above, as per JAXWS client model. + *
  • avoid unnecessary WSDL resolution through a shared LRU cache. + *
  • configure stub implementations with JAXWS handlers which turn outgoing service calls into gCore calls (i.e. add headers for current scope and service coordinates). + *
+ * + * Note that all factories can be configured to go through a proxy, e.g. for debugging purposes (cf. + * {@link StubFactory#setProxy(String, int)}). + *

+ * + * @author Fabio Simeoni + * @see StubCache + * @see GCoreJAXWSHandler + */ +public class StubFactory implements StubFactoryDSL.AtClause { + + private static final Logger log = LoggerFactory.getLogger(StubFactory.class); + + // note that using the standard http.hostProxy and http.hostPort would have been clearner, but it creates problems, + // for example when wsdls with imports must be resolved. better to offer ad-hoc support with confined effect in the JVM. + private static String proxyHost; + private static int proxyPort; + + private static StubCache cache = new StubCache(); + + private final GCoreService target; + + /** + * Creates an instance for a given {@link GCoreService}. + * + * @param target the service + */ + public StubFactory(GCoreService target) { + + notNull("gCore Service", target); + + this.target = target; + } + + public T at(EndpointReference reference) { + + notNull("instance reference", reference); + + GCoreEndpointReference epr = new GCoreEndpointReference(reference); + + String proxied = setProxyOn(epr.address); + + W3CEndpointReferenceBuilder builder = new W3CEndpointReferenceBuilder().address(proxied); + + reference = epr.key==null? + builder.build(): + builder.referenceParameter(epr.key).build(); + + return at(proxied,reference,new AddressingFeature()); + + } + + public T at(URI address) { + + notNull("endpoint address", address); + + //build reference from address + EndpointReference reference = new W3CEndpointReferenceBuilder().address(address.toString()).build(); + + return at(reference); + } + + private T at(String endpointAddress, EndpointReference reference, AddressingFeature ... features) { + + try { + + // get JAXWS service from endpoint address + Service service = buildService(endpointAddress+"?wsdl", target.qName()); + + // get JAXWS stub + T stub = service.getPort(reference,target.type(),features); + + BindingProvider provider = (BindingProvider) stub; + + // configure stub for gCube calls + registerHandler(provider, target); + + return stub; + } catch (Exception e) { + throw new RuntimeException("could not configure discovery service", e); + } + } + + /** + * Creates a stub for a given {@link GCoreService} + * + * @param service information about the service + * @return the next clause for the creation of the stub + */ + public static AtClause stubFor(GCoreService service) { + return new StubFactory(service); + } + + // helper + private synchronized Service buildService(final String wsdlAddress, final QName name) throws Exception { + + Callable task = new Callable() { + @Override + public Service call() throws Exception { + log.info("fetching wsdl for {} at {}", name.getLocalPart(),wsdlAddress); + return Service.create(new URL(wsdlAddress), name); + } + }; + + Service service = cache.get(name,task); + + return service; + } + + // helper + private void registerHandler(BindingProvider provider, GCoreService context) { + + Binding binding = provider.getBinding(); + + @SuppressWarnings("rawtypes") + List currentChain = binding.getHandlerChain(); + + GCoreJAXWSHandler handler = new GCoreJAXWSHandler(context); + + currentChain.add(handler); + + binding.setHandlerChain(currentChain); + + } + + /** + * Configures a proxy for client interactions through this factory. + * + * @param host the proxy host + * @return port the proxy port + */ + public static void setProxy(String host, int port) { + StubFactory.proxyHost = host; + StubFactory.proxyPort = port; + } + + // helper + private String setProxyOn(String address) { + + if (proxyHost != null) + try { + //pass through URI for replacing host and port with proxy's + URI u = URI.create(address); + return new URI(u.getScheme(), u.getUserInfo(), proxyHost, Integer.valueOf(proxyPort), u.getPath(), + u.getQuery(), u.getFragment()).toString(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + return address; + } + +} \ No newline at end of file diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactoryDSL.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactoryDSL.java new file mode 100644 index 0000000..4e91c36 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactoryDSL.java @@ -0,0 +1,39 @@ +package org.gcube.common.clients.stubs.jaxws; + +import java.net.URI; + +import javax.xml.ws.EndpointReference; + +/** + * Simple DSL for the {@link StubFactory} + * + * @author Fabio Simeoni + * + */ +public interface StubFactoryDSL { + + /** + * Selects the address of the service endpoint or service instance. + * + * @author Fabio Simeoni + * + * @param + */ + interface AtClause { + + /** + * Returns a stub for a service endpoint at a given address. + * @param address the address + * @return the stub + */ + T at(URI address); + + + /** + * Returns a stub for a service endpoint or service instance at a given address. + * @param ref a reference to the endpoint or instance + * @return the stub + */ + T at(EndpointReference ref); + } +} diff --git a/src/test/java/org/acme/StubTest.java b/src/test/java/org/acme/StubTest.java new file mode 100644 index 0000000..414568f --- /dev/null +++ b/src/test/java/org/acme/StubTest.java @@ -0,0 +1,258 @@ +package org.acme; + +import static org.acme.jaxws.stubs.StatefulStub.*; +import static org.acme.jaxws.stubs.StatelessStub.*; +import static org.gcube.common.clients.stubs.jaxws.StubFactory.*; +import static org.junit.Assert.*; + +import java.net.URI; +import java.util.Arrays; +import java.util.List; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.ws.EndpointReference; +import javax.xml.ws.soap.SOAPFaultException; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.acme.jaxws.stubs.BarInput; +import org.acme.jaxws.stubs.BarOutput; +import org.acme.jaxws.stubs.FooException; +import org.acme.jaxws.stubs.StatefulStub; +import org.acme.jaxws.stubs.StatelessStub; +import org.acme.jaxws.stubs.Types.AnyElement; +import org.acme.jaxws.stubs.Types.ChoiceOne; +import org.acme.jaxws.stubs.Types.PolyWrapped; +import org.acme.jaxws.stubs.Types.Sometype; +import org.acme.jaxws.stubs.Types.Subone; +import org.acme.jaxws.stubs.Types.Subtwo; +import org.acme.jaxws.stubs.VoidWrapper; +import org.gcube.common.clients.stubs.jaxws.JAXWSUtils; +import org.gcube.common.mycontainer.Deployment; +import org.gcube.common.mycontainer.Gar; +import org.gcube.common.mycontainer.MyContainerTestRunner; +import org.gcube.common.scope.api.ScopeProvider; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +@RunWith(MyContainerTestRunner.class) +public class StubTest { + + @Deployment + static Gar testservice = new Gar("test-service").addConfigurations("src/test/resources/test-service/etc").addInterfaces("src/test/resources/test-service/wsdl"); + + static StatelessStub stub; + + @BeforeClass + public static void setup() { + + //setProxy("localhost",8081); //comment after on-the-wire analysis + + ScopeProvider.instance.set("/gcube/devsec"); + + stub = stubFor(stateless).at(URI.create("http://localhost:9999/wsrf/services/acme/service/stateless")); + + } + + @Test + public void fooTest() { + + String input = "input"; + String output = stub.foo(input); + + assertEquals(input, output); + } + + @Test + public void fooWrappedTest() { + + String input = "input"; + String output = stub.fooWrapped(input); + + assertEquals(input, output); + } + + + @Test + public void fooMixedTest() { + + String input = "input"; + String output = stub.fooMixed(input).ret; + + assertEquals(input, output); + } + + @Test + public void fooBulkTest() { + + String[] input = new String[]{"1","2","3"}; + String output = stub.fooBulk(Arrays.asList(input)); + + assertEquals(Arrays.deepToString(input), output); + } + + @Test + public void fooContingencyTest() { + + try { + stub.fooFault("contingency"); + fail(); + } + catch(FooException e) { + + } + + } + + @Test + public void fooOutageTest() throws Exception { + + try { + stub.fooFault("outage"); + fail(); + } + catch(SOAPFaultException e) { + + } + + } + + @Test + public void fooProperOutageTest() throws Exception { + + try { + stub.fooFault("proper"); + fail(); + } + catch(SOAPFaultException e) { + new RuntimeException(JAXWSUtils.remoteCause(e)).printStackTrace(); + } + + } + + @Test + public void barTest() throws Exception { + + String input = "input"; + BarInput request = new BarInput(); + request.in1 = input; + request.in2 = 3; + BarOutput response = stub.bar(request); + + List expected = Arrays.asList(input,input); + + assertEquals(expected, response.output); + + } + + @Test + public void bazTest() { + + String response = stub.baz(new VoidWrapper()); + + assertNotNull(response); + + } + + @Test + public void nothingTest() { + + stub.nothing(); + + } + + @Test + public void barWrappedTest() throws Exception { + + String in1 = "input"; + Integer in2 = 3; + List response = stub.barWrapped(in1,in2); + + List expected = Arrays.asList(in1,in1); + + assertEquals(expected, response); + + } + + @Test + public void createWithEPRTest() throws Exception { + + W3CEndpointReference ref = stub.create("input"); + + assertNotNull(ref); + + } + + @Test + public void callsTest() throws Exception { + + EndpointReference ref = stub.create("input"); + + StatefulStub stub = stubFor(stateful).at(ref); + + assertNotNull(stub.calls()); + + + } + + @Test + public void anyTypeTest() throws Exception { + + String ref = stub.any("input"); + + assertEquals(String.class.getName(),ref); + + Sometype st = new Sometype(); + st.some="hello"; + + ref = stub.any(st); + + assertEquals(org.acme.sample.stubs.Sometype.class.getName(),ref); + } + + @Test + public void anyElementTest() throws Exception { + + Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element root = d.createElement("root"); + root.appendChild(d.createElement("empty")); + root.setAttribute("foo", "val"); + AnyElement e = new AnyElement(); + e.some=root; + stub.anyElement(e); + } + + @Test + public void poly() throws Exception { + + stub.poly(new Subone()); + stub.poly(new Subtwo()); + + } + + @Test + public void polyWrapped() throws Exception { + + Subone one = new Subone(); + one.one="one"; + PolyWrapped wrapped = new PolyWrapped(); + wrapped.param=one; + stub.polyWrapped(wrapped); + + + } + + @Test + public void choice() throws Exception { + + Subone sub = new Subone(); + ChoiceOne one = new ChoiceOne(); + one.one=sub; + stub.choice(one); + + + } + +} diff --git a/src/test/java/org/acme/jaxws/stubs/BarInput.java b/src/test/java/org/acme/jaxws/stubs/BarInput.java new file mode 100644 index 0000000..b2caee9 --- /dev/null +++ b/src/test/java/org/acme/jaxws/stubs/BarInput.java @@ -0,0 +1,15 @@ +package org.acme.jaxws.stubs; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + + +@XmlRootElement +public class BarInput { + + @XmlElement + public String in1; + + @XmlElement + public int in2; +} diff --git a/src/test/java/org/acme/jaxws/stubs/BarOutput.java b/src/test/java/org/acme/jaxws/stubs/BarOutput.java new file mode 100644 index 0000000..588f4b8 --- /dev/null +++ b/src/test/java/org/acme/jaxws/stubs/BarOutput.java @@ -0,0 +1,13 @@ +package org.acme.jaxws.stubs; + +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class BarOutput { + + @XmlElement + public List output; +} diff --git a/src/test/java/org/acme/jaxws/stubs/FooException.java b/src/test/java/org/acme/jaxws/stubs/FooException.java new file mode 100644 index 0000000..2efed5a --- /dev/null +++ b/src/test/java/org/acme/jaxws/stubs/FooException.java @@ -0,0 +1,13 @@ +package org.acme.jaxws.stubs; + +import javax.xml.ws.WebFault; + +@WebFault(name="SampleFault") +public class FooException extends Exception { + + private static final long serialVersionUID = 1L; + + public FooException(String s) { + super(s); + } +} diff --git a/src/test/java/org/acme/jaxws/stubs/StatefulStub.java b/src/test/java/org/acme/jaxws/stubs/StatefulStub.java new file mode 100644 index 0000000..c646118 --- /dev/null +++ b/src/test/java/org/acme/jaxws/stubs/StatefulStub.java @@ -0,0 +1,33 @@ +package org.acme.jaxws.stubs; + +import static org.acme.jaxws.stubs.StatefulStub.*; +import static org.gcube.common.clients.stubs.jaxws.GCoreServiceBuilder.*; + +import javax.jws.WebResult; +import javax.jws.WebService; +import javax.xml.namespace.QName; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +@WebService(name=porttype,targetNamespace=porttypeNS) +public interface StatefulStub { + + public static final String namespace = "http://acme.org/service"; + public static final String localname = "StatefulService"; + public static final QName name = new QName(namespace,localname); + + public static final String porttypeNS = "http://acme.org"; + static final String porttype = "StatefulPortType"; + static final String port = "StatefulPortTypePort"; + + public static String service_class="samples"; + public static String service_name="test-service"; + + static final GCoreService stateful = service(). + withName(name). + coordinates(service_class,service_name). + andInterface(StatefulStub.class); + + @WebResult(name="output") + String calls(); +} diff --git a/src/test/java/org/acme/jaxws/stubs/StatelessStub.java b/src/test/java/org/acme/jaxws/stubs/StatelessStub.java new file mode 100644 index 0000000..18c0afb --- /dev/null +++ b/src/test/java/org/acme/jaxws/stubs/StatelessStub.java @@ -0,0 +1,82 @@ +package org.acme.jaxws.stubs; + +import static javax.jws.soap.SOAPBinding.ParameterStyle.*; +import static org.acme.jaxws.stubs.StatelessStub.*; +import static org.gcube.common.clients.stubs.jaxws.GCoreServiceBuilder.*; + +import java.util.List; + +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebResult; +import javax.jws.WebService; +import javax.jws.soap.SOAPBinding; +import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.namespace.QName; +import javax.xml.ws.wsaddressing.W3CEndpointReference; + +import org.acme.jaxws.stubs.Types.AnyElement; +import org.acme.jaxws.stubs.Types.Base; +import org.acme.jaxws.stubs.Types.Choice; +import org.acme.jaxws.stubs.Types.MixedWrapper; +import org.acme.jaxws.stubs.Types.PolyWrapped; +import org.acme.jaxws.stubs.Types.Sometype; +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +@WebService(name=porttypeLN,targetNamespace=porttypeNS) +@SOAPBinding(parameterStyle=BARE) +@XmlSeeAlso(Sometype.class) +public interface StatelessStub { + + public static final QName name = new QName("http://acme.org/service","StatelessService"); + + public static final String porttypeNS = "http://acme.org"; + static final String porttypeLN = "StatelessPortType"; + + public static String service_class="samples"; + public static String service_name="test-service"; + + static final GCoreService stateless = service(). + withName(name). + coordinates(service_class,service_name). + andInterface(StatelessStub.class); + + String foo(String s); + + @SOAPBinding(parameterStyle=WRAPPED) + @WebResult(name="anything") + String fooWrapped(@WebParam(name="param") String s); + + MixedWrapper fooMixed(String s); + + @SOAPBinding(parameterStyle=WRAPPED) + @WebResult(name="return") + String fooBulk(@WebParam(name="param") List elements); + + String fooFault(String s) throws FooException; + + String baz(VoidWrapper v); + + @SOAPBinding(parameterStyle=WRAPPED) + void nothing(); + + BarOutput bar(BarInput s); + + @SOAPBinding(parameterStyle=WRAPPED) + @WebMethod(operationName="bar") + @WebResult(name="output") + List barWrapped(@WebParam(name="in1")String s,@WebParam(name="in2")int i); + + W3CEndpointReference create(String s); + + String any(Object o); + + void anyElement(AnyElement e); + + void poly(Base one); + + void polyWrapped(PolyWrapped one); + + public void choice(Choice c); + +} diff --git a/src/test/java/org/acme/jaxws/stubs/Types.java b/src/test/java/org/acme/jaxws/stubs/Types.java new file mode 100644 index 0000000..fe8c91e --- /dev/null +++ b/src/test/java/org/acme/jaxws/stubs/Types.java @@ -0,0 +1,61 @@ +package org.acme.jaxws.stubs; + +import javax.xml.bind.annotation.XmlAnyElement; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + +import org.w3c.dom.Element; + +public class Types { + + public static class MixedWrapper{ + @XmlElement(name="anything") + public String ret; + } + + public static class AnyElement { + @XmlAnyElement + public Element some; + } + + public static class Sometype { + @XmlElement + public String some; + } + + public static class SomeElement { + @XmlAnyElement + public Element some; + } + + @XmlRootElement + public static class PolyWrapped { + @XmlElement + public Base param; + } + + @XmlSeeAlso({Subone.class,Subtwo.class}) + public static class Base{} + + + @XmlRootElement + public static class Subone extends Base { + @XmlElement + public String one; + } + + @XmlRootElement + public static class Subtwo extends Base { + @XmlElement + public String two; + } + + @XmlSeeAlso({ChoiceOne.class}) + public static abstract class Choice {} + + public static class ChoiceOne extends Choice { + @XmlElement + public Subone one; + } +} diff --git a/src/test/java/org/acme/jaxws/stubs/VoidWrapper.java b/src/test/java/org/acme/jaxws/stubs/VoidWrapper.java new file mode 100644 index 0000000..689087b --- /dev/null +++ b/src/test/java/org/acme/jaxws/stubs/VoidWrapper.java @@ -0,0 +1,8 @@ +package org.acme.jaxws.stubs; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class VoidWrapper { + +} diff --git a/src/test/java/org/acme/service/Factory.java b/src/test/java/org/acme/service/Factory.java new file mode 100644 index 0000000..f1ac2ca --- /dev/null +++ b/src/test/java/org/acme/service/Factory.java @@ -0,0 +1,39 @@ +package org.acme.service; + +import org.apache.axis.message.addressing.EndpointReferenceType; +import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext; +import org.gcube.common.core.faults.GCUBEFault; +import org.gcube.common.core.faults.GCUBEUnrecoverableException; +import org.gcube.common.core.porttypes.GCUBEPortType; +import org.gcube.common.core.state.GCUBEWSHome; +import org.gcube.common.core.state.GCUBEWSResource; +import org.gcube.common.core.state.GCUBEWSResourceKey; +import org.gcube.common.core.utils.logging.GCUBELog; + +public class Factory extends GCUBEPortType { + + GCUBELog logger = new GCUBELog(this); + + + @Override + protected ServiceContext getServiceContext() { + return ServiceContext.getContext(); + } + + + public EndpointReferenceType create(String name) throws GCUBEFault { + //create/reuse the resource + try { + GCUBEStatefulPortTypeContext ptcxt = StatefulContext.getContext(); + GCUBEWSHome home = ptcxt.getWSHome(); + GCUBEWSResourceKey key = ptcxt.makeKey(name); + GCUBEWSResource ws = home.create(key,name); + ws.store(); + return ws.getEPR(); + + } catch (Exception e) { + logger.error("unable to logon", e); + throw new GCUBEUnrecoverableException(e).toFault(); + } + } +} diff --git a/src/test/java/org/acme/service/Home.java b/src/test/java/org/acme/service/Home.java new file mode 100644 index 0000000..edfab43 --- /dev/null +++ b/src/test/java/org/acme/service/Home.java @@ -0,0 +1,11 @@ +package org.acme.service; + +import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext; +import org.gcube.common.core.state.GCUBEWSHome; + +public class Home extends GCUBEWSHome { + + @Override + public GCUBEStatefulPortTypeContext getPortTypeContext() {return StatefulContext.getContext();} + +} diff --git a/src/test/java/org/acme/service/Resource.java b/src/test/java/org/acme/service/Resource.java new file mode 100644 index 0000000..581de7e --- /dev/null +++ b/src/test/java/org/acme/service/Resource.java @@ -0,0 +1,50 @@ +package org.acme.service; + +import org.gcube.common.core.state.GCUBEWSResource; +import org.globus.wsrf.ResourceProperty; + +public class Resource extends GCUBEWSResource { + + + + private static final String NAME_RP_NAME = "Name"; + /** Client visits.*/ + int calls; + /** Client name. */ + String name; + + /**{@inheritDoc}*/ + public void initialise(Object... args) throws Exception { + if (args == null || args.length!=1) throw new IllegalArgumentException(); + this.setName((String) args[0]); + } + + /** Returns the number of client visits. + * @return the visits.*/ + public synchronized int getVisits() {return calls;} + + /** Returns the client name. + * @return the name.*/ + public synchronized String getName() { + return (String) this.getResourcePropertySet().get(NAME_RP_NAME).get(0); + + } + + /** Sets the client name. + * @params the name.*/ + public synchronized void setName(String name) { + ResourceProperty property = this.getResourcePropertySet().get(NAME_RP_NAME); + property.clear(); + property.add(name); + } + + /**Sets the number of client visits. + * the visits.*/ + protected synchronized void addVisit() {this.calls++;} + + @Override + protected String[] getPropertyNames() { + return new String[]{NAME_RP_NAME}; + } + +} diff --git a/src/test/java/org/acme/service/ServiceContext.java b/src/test/java/org/acme/service/ServiceContext.java new file mode 100644 index 0000000..686abca --- /dev/null +++ b/src/test/java/org/acme/service/ServiceContext.java @@ -0,0 +1,25 @@ +package org.acme.service; + +import static org.acme.service.Utils.*; + +import org.gcube.common.core.contexts.GCUBEServiceContext; + +public class ServiceContext extends GCUBEServiceContext { + + + /** Single context instance, created eagerly */ + private static ServiceContext cache = new ServiceContext(); + + /** Returns cached instance */ + public static ServiceContext getContext() {return cache;} + + /** Prevents accidental creation of more instances */ + private ServiceContext(){}; + + /** {@inheritDoc} */ + protected String getJNDIName() {return NAME;} + + + + +} diff --git a/src/test/java/org/acme/service/Stateful.java b/src/test/java/org/acme/service/Stateful.java new file mode 100644 index 0000000..9d86d54 --- /dev/null +++ b/src/test/java/org/acme/service/Stateful.java @@ -0,0 +1,35 @@ +package org.acme.service; + +import org.acme.sample.stubs.CallsResponse; +import org.gcube.common.core.porttypes.GCUBEPortType; +import org.gcube.common.core.types.VOID; +import org.globus.wsrf.ResourceException; + +public class Stateful extends GCUBEPortType { + + @Override + protected ServiceContext getServiceContext() {return ServiceContext.getContext();} + + public CallsResponse calls(VOID voidType) { + try { + Resource resource = this.getResource(); + return new CallsResponse(resource.getVisits()+" for "+resource.getName()); + } + catch (Exception e) { + throw new RuntimeException("problem",e); + } + + } + + /** + * + * @return the stateful resource + * @throws ResourceException if no resource was found in the current context + */ + private Resource getResource() throws ResourceException { + return (Resource) StatefulContext.getContext().getWSHome().find(); + } + + + +} diff --git a/src/test/java/org/acme/service/StatefulContext.java b/src/test/java/org/acme/service/StatefulContext.java new file mode 100644 index 0000000..bf19723 --- /dev/null +++ b/src/test/java/org/acme/service/StatefulContext.java @@ -0,0 +1,34 @@ +package org.acme.service; + +import static org.acme.service.Utils.*; + +import org.gcube.common.core.contexts.GCUBEServiceContext; +import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext; + +public class StatefulContext extends GCUBEStatefulPortTypeContext { + + + /** Single context instance, created eagerly */ + private static GCUBEStatefulPortTypeContext cache = new StatefulContext(); + + /**Create an instance, privately */ + private StatefulContext(){} + + /** Returns singleton context. + * @return the context. */ + public static GCUBEStatefulPortTypeContext getContext() {return cache;} + + /** {@inheritDoc} **/ + public String getJNDIName() {return STATEFUL_NAME;} + + /** {@inheritDoc} **/ + public String getNamespace() {return NS;} + + /** {@inheritDoc} **/ + public GCUBEServiceContext getServiceContext() {return ServiceContext.getContext();} + + + + + +} diff --git a/src/test/java/org/acme/service/Stateless.java b/src/test/java/org/acme/service/Stateless.java new file mode 100644 index 0000000..71252a1 --- /dev/null +++ b/src/test/java/org/acme/service/Stateless.java @@ -0,0 +1,158 @@ +/** + * + */ +package org.acme.service; + +import java.io.StringWriter; +import java.util.Arrays; + +import javax.xml.namespace.QName; + +import org.acme.sample.stubs.AnyElement; +import org.acme.sample.stubs.AnyElementResponse; +import org.acme.sample.stubs.Bar; +import org.acme.sample.stubs.BarResponse; +import org.acme.sample.stubs.Base; +import org.acme.sample.stubs.Choice; +import org.acme.sample.stubs.ChoiceResponse; +import org.acme.sample.stubs.FooBulk; +import org.acme.sample.stubs.FooBulkResponse; +import org.acme.sample.stubs.FooWrapped; +import org.acme.sample.stubs.FooWrappedResponse; +import org.acme.sample.stubs.Nothing; +import org.acme.sample.stubs.PolyResponse; +import org.acme.sample.stubs.PolyWrapped; +import org.acme.sample.stubs.SampleFault; +import org.apache.axis.message.addressing.EndpointReferenceType; +import org.gcube.common.core.contexts.GCUBEServiceContext; +import org.gcube.common.core.contexts.GCUBEStatefulPortTypeContext; +import org.gcube.common.core.faults.FaultUtils; +import org.gcube.common.core.faults.GCUBEFault; +import org.gcube.common.core.porttypes.GCUBEPortType; +import org.gcube.common.core.state.GCUBEWSHome; +import org.gcube.common.core.state.GCUBEWSResource; +import org.gcube.common.core.state.GCUBEWSResourceKey; +import org.gcube.common.core.types.VOID; +import org.globus.wsrf.encoding.ObjectSerializer; + +/** + * @author Fabio Simeoni + * + */ +public class Stateless extends GCUBEPortType { + + public String foo(String s) { + + return s; + } + + public FooWrappedResponse fooWrapped(FooWrapped wrapped) { + + return new FooWrappedResponse(wrapped.getParam()); + } + + public FooWrappedResponse fooMixed(String s) { + + return new FooWrappedResponse(s); + } + + public FooBulkResponse fooBulk(FooBulk bulk) { + + System.out.println(Arrays.asList(bulk.getParam())); + + return new FooBulkResponse(Arrays.deepToString(bulk.getParam())); + } + + public Nothing nothing(Nothing n) { + System.err.println("invoked NOTHING "+n); + return new Nothing(); + } + + public String fooFault(String flag) throws SampleFault, GCUBEFault { + + if (flag.equals("contingency")) + throw new SampleFault(); + else if (flag.equals("proper")) + throw FaultUtils.newFault(new GCUBEFault(), new RuntimeException("generic problem")); + else + throw new RuntimeException("generic problem"); + } + + public BarResponse bar(Bar s) { + + return new BarResponse(new String[] { s.getIn1(), s.getIn1() }); + } + + public String baz(VOID v) { + + return "called"; + } + + public EndpointReferenceType create(String input) { + + try { + GCUBEStatefulPortTypeContext ptcxt = StatefulContext.getContext(); + GCUBEWSHome home = ptcxt.getWSHome(); + GCUBEWSResourceKey key = ptcxt.makeKey(input); + GCUBEWSResource ws = home.create(key, input); + ws.store(); + return ws.getEPR(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String createAsString(String input) { + + try { + GCUBEStatefulPortTypeContext ptcxt = StatefulContext.getContext(); + GCUBEWSHome home = ptcxt.getWSHome(); + GCUBEWSResourceKey key = ptcxt.makeKey(input); + GCUBEWSResource ws = home.create(key, input); + ws.store(); + + StringWriter w = new StringWriter(); + ObjectSerializer.serialize(w,ws.getEPR(), new QName("http://foo","test")); + + return w.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + public String any(Object o) { + System.err.println(o); + return o.getClass().getName(); + } + + public AnyElementResponse anyElement(AnyElement o) { + System.out.println(Arrays.deepToString(o.get_any())); + return new AnyElementResponse(); + } + + public PolyResponse poly(Base b) { + System.err.println(b.getClass()); + return new PolyResponse(); + } + + public PolyResponse polyWrapped(PolyWrapped w) { + System.err.println(w.getParam().getClass()); + return new PolyResponse(); + } + + public ChoiceResponse choice(Choice c) { + System.err.println("one:"+c.getOne()); + System.err.println("two:"+c.getTwo()); + return new ChoiceResponse(); + } + + /** {@inheritDoc} */ + @Override + public GCUBEServiceContext getServiceContext() { + return ServiceContext.getContext(); + } + + + +} diff --git a/src/test/java/org/acme/service/StatelessContext.java b/src/test/java/org/acme/service/StatelessContext.java new file mode 100644 index 0000000..c51accd --- /dev/null +++ b/src/test/java/org/acme/service/StatelessContext.java @@ -0,0 +1,28 @@ +package org.acme.service; + +import static org.acme.service.Utils.*; + +import org.gcube.common.core.contexts.GCUBEPortTypeContext; +import org.gcube.common.core.contexts.GCUBEServiceContext; + +public class StatelessContext extends GCUBEPortTypeContext { + + + /** Single context instance, created eagerly */ + private static StatelessContext cache = new StatelessContext(); + + private StatelessContext(){} + + /** Returns cached instance */ + public static StatelessContext getContext() {return cache;} + + /**{@inheritDoc}*/ + public String getJNDIName() {return STATELESS_NAME;} + + /** {@inheritDoc}*/ + public String getNamespace() {return NS;} + + /** {@inheritDoc}*/ + public GCUBEServiceContext getServiceContext() {return ServiceContext.getContext();} + +} diff --git a/src/test/java/org/acme/service/Utils.java b/src/test/java/org/acme/service/Utils.java new file mode 100644 index 0000000..2ab5d65 --- /dev/null +++ b/src/test/java/org/acme/service/Utils.java @@ -0,0 +1,17 @@ +/** + * + */ +package org.acme.service; + +/** + * @author Fabio Simeoni + * + */ +public class Utils { + + public static final String NS="http://acme.org/service"; + public static final String NAME="acme/service"; + + public static final String STATELESS_NAME="acme/service/stateless"; + public static final String STATEFUL_NAME="acme/service/stateful"; +} diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties new file mode 100644 index 0000000..305cc41 --- /dev/null +++ b/src/test/resources/log4j.properties @@ -0,0 +1,10 @@ +log4j.appender.ROOT=org.apache.log4j.ConsoleAppender +log4j.appender.ROOT.layout=org.apache.log4j.PatternLayout +log4j.appender.ROOT.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %c{2} [%t,%M:%L] %m%n +log4j.rootLogger=DEBUG,ROOT + +log4j.appender.SAMPLE=org.apache.log4j.ConsoleAppender +log4j.appender.SAMPLE.layout=org.apache.log4j.PatternLayout +log4j.appender.SAMPLE.layout.ConversionPattern=[SAMPLE] %d{HH:mm:ss,SSS} %-5p %c{2} [%t,%M:%L] %m%n + +log4j.category.org.acme=TRACE,SAMPLE \ No newline at end of file diff --git a/src/test/resources/test-service/etc/deploy-jndi-config.xml b/src/test/resources/test-service/etc/deploy-jndi-config.xml new file mode 100755 index 0000000..7668b61 --- /dev/null +++ b/src/test/resources/test-service/etc/deploy-jndi-config.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + factory + org.globus.wsrf.jndi.BeanFactory + + + resourceClass + org.acme.service.Resource + + + + + + + diff --git a/src/test/resources/test-service/etc/deploy-server.wsdd b/src/test/resources/test-service/etc/deploy-server.wsdd new file mode 100644 index 0000000..cc104f4 --- /dev/null +++ b/src/test/resources/test-service/etc/deploy-server.wsdd @@ -0,0 +1,32 @@ + + + + + share/schema/test-service/Stateless_service.wsdl + + + + + + + + + + + + + + + + share/schema/test-service/Stateful_service.wsdl + + + + + + + + diff --git a/src/test/resources/test-service/etc/profile.xml b/src/test/resources/test-service/etc/profile.xml new file mode 100644 index 0000000..1921231 --- /dev/null +++ b/src/test/resources/test-service/etc/profile.xml @@ -0,0 +1,39 @@ + + + + Service + + samples + test-service + 1.0.0 + +

+ test-service + 1.0.0-SNAPSHOT + + org.gcube.samples + test-service + 1.0.0-SNAPSHOT + + test-service-1.0.0-SNAPSHOT.gar + + acme/sample/stateless + +
+ + test-service-stubs + 1.0.0-SNAPSHOT + + org.gcube.samples + test-service-stubs + 1.0.0-SNAPSHOT + + library + + test-service-stubs-1.0.0-SNAPSHOT.jar + + + + + + diff --git a/src/test/resources/test-service/wsdl/Stateful.wsdl b/src/test/resources/test-service/wsdl/Stateful.wsdl new file mode 100644 index 0000000..5376b1d --- /dev/null +++ b/src/test/resources/test-service/wsdl/Stateful.wsdl @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/test-service/wsdl/Stateless.wsdl b/src/test/resources/test-service/wsdl/Stateless.wsdl new file mode 100644 index 0000000..4fd9668 --- /dev/null +++ b/src/test/resources/test-service/wsdl/Stateless.wsdl @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +