diff --git a/distro/changelog.xml b/distro/changelog.xml index f09bc6d..85d2c92 100644 --- a/distro/changelog.xml +++ b/distro/changelog.xml @@ -1,5 +1,9 @@ - + First Release + + Modular handler discovery and delegation + Fix: WSDL cache now releases the lock it acquires when clearing + \ No newline at end of file diff --git a/pom.xml b/pom.xml index da5018c..a87e7b1 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.gcube.core common-gcore-stubs - 1.0.0-SNAPSHOT + 1.1.0-SNAPSHOT GCore Stubs JAXWS Stub Support for gCore Service @@ -59,7 +59,7 @@ org.gcube.tools my-container - 1.0.0 + 2.0.0-SNAPSHOT test @@ -67,7 +67,7 @@ org.gcube.tools my-container - 1.0.0 + 2.0.0-SNAPSHOT tar.gz distro test 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 deleted file mode 100644 index 4c55e76..0000000 --- a/src/main/java/org/gcube/common/clients/stubs/jaxws/GCoreJAXWSHandler.java +++ /dev/null @@ -1,127 +0,0 @@ -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/StubCache.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubCache.java index 7980393..4f2468d 100644 --- a/src/main/java/org/gcube/common/clients/stubs/jaxws/StubCache.java +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubCache.java @@ -35,8 +35,9 @@ class StubCache { //obtain a lock for current key Lock nameLock = lockFor(name); + nameLock.lock(); + try { - cache.remove(name); } finally { 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 index fd4ff8d..6db647a 100644 --- a/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactory.java +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/StubFactory.java @@ -18,6 +18,7 @@ import javax.xml.ws.soap.AddressingFeature; import javax.xml.ws.wsaddressing.W3CEndpointReferenceBuilder; import org.gcube.common.clients.stubs.jaxws.StubFactoryDSL.AtClause; +import org.gcube.common.clients.stubs.jaxws.handlers.GCoreJAXWSHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/AbstractHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/AbstractHandler.java new file mode 100644 index 0000000..1de19b4 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/AbstractHandler.java @@ -0,0 +1,36 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPException; +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +/** + * Adapter implementation of {@link CallHandler} for selective implementations (handle only outgoing calls, or only responses). + * + * @author Fabio Simeoni + * + */ +public class AbstractHandler implements CallHandler { + + @Override + public void handleRequest(GCoreService target, SOAPHeader header, SOAPMessageContext context) throws Exception { + } + + @Override + public void handleResponse(GCoreService target, SOAPMessageContext context) throws Exception { + } + + /** + * Helper to set an element on the SOAP header of the outgoing call + * @param header the SOAP header + * @param name the name the element's name + * @param value the element's value + * @throws SOAPException if the element cannot be added to the header + */ + protected final void addHeader(SOAPHeader header,QName name, String value) throws SOAPException { + header.addHeaderElement(name).addTextNode(value); + } +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/CallHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/CallHandler.java new file mode 100644 index 0000000..65c493e --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/CallHandler.java @@ -0,0 +1,32 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +/** + * Handles outgoing calls and their responses. + * + * @author Fabio Simeoni + * + */ +public interface CallHandler { + + /** + * Handles an outgoing call. + * @param target information about the target service + * @param header the SOAP header of the call + * @param context the JAX-WS call context + * @throws Exception if the call cannot be handled + */ + void handleRequest(GCoreService target,SOAPHeader header,SOAPMessageContext context) throws Exception; + + /** + * Handles the response to a call. + * @param target information about the target service + * @param context the JAX-WS call context + * @throws Exception if the response cannot be handled + */ + void handleResponse(GCoreService target,SOAPMessageContext context) throws Exception; +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/ClientInfoHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/ClientInfoHandler.java new file mode 100644 index 0000000..37fa209 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/ClientInfoHandler.java @@ -0,0 +1,29 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +/** + * A {@link CallHandler} that sets client identification on outgoing calls. + * + * @author Fabio Simeoni + * + */ +public class ClientInfoHandler extends AbstractHandler { + + + /** 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); + + @Override + public void handleRequest(GCoreService target, SOAPHeader header, SOAPMessageContext context) throws Exception { + addHeader(header,CALLER_QNAME, target.clientId()); + } +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/GCoreJAXWSHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/GCoreJAXWSHandler.java new file mode 100644 index 0000000..20c8b6a --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/GCoreJAXWSHandler.java @@ -0,0 +1,69 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import java.util.Set; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.handler.soap.SOAPHandler; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +/** + * A {@link SOAPHandler} that adds gCube headers to outgoing calls. + * + * @author Fabio Simeoni + * + */ +public class GCoreJAXWSHandler implements SOAPHandler { + + private final GCoreService target; + + public 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(); + + for (CallHandler handler : HandlerRegistry.handlers()) + handler.handleRequest(target, header, context); + + } catch (Exception e) { + throw new RuntimeException("cannot configure outgoing message", e); + } + else + try { + + for (CallHandler handler : HandlerRegistry.handlers()) + handler.handleResponse(target, context); + + } 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) { + } + +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/HandlerRegistry.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/HandlerRegistry.java new file mode 100644 index 0000000..b581600 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/HandlerRegistry.java @@ -0,0 +1,45 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovers available {@link CallHandler}s. + * + * @author Fabio Simeoni + * + */ +public class HandlerRegistry { + + private final static Logger log = LoggerFactory.getLogger(HandlerRegistry.class); + + private final static List handlers = new ArrayList(); + + static { + + ServiceLoader loader = ServiceLoader.load(CallHandler.class); + Iterator it = loader.iterator(); + while (it.hasNext()) + try { + CallHandler handler = it.next(); + log.info("loaded call handler {}",handler); + handlers.add(handler); + } + catch(Error e) { + log.error("could not load call handler",e); + } + } + + /** + * Returns the discovered {@link CallHandler}s. + * @return the handlers + */ + public static List handlers() { + return handlers; + } +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/LegacyWSAddressingHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/LegacyWSAddressingHandler.java new file mode 100644 index 0000000..a31bc4d --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/LegacyWSAddressingHandler.java @@ -0,0 +1,33 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import java.util.Iterator; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPHeader; +import javax.xml.soap.SOAPHeaderElement; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +/** + * A {@link CallHandler} that transforms WS-Addressing information in outgoing calls into MemberSubmission form. + * @author Fabio Simeoni + * + */ +public class LegacyWSAddressingHandler extends AbstractHandler { + + //helper: adapts ws-addressing headers to member submission's. brutal but there is no support for member submission in + //jdk 1.6 + @Override + public void handleRequest(GCoreService target, SOAPHeader header, SOAPMessageContext context) 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()); + } + + } + } +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/ScopeHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/ScopeHandler.java new file mode 100644 index 0000000..eb510a0 --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/ScopeHandler.java @@ -0,0 +1,31 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; +import org.gcube.common.scope.api.ScopeProvider; + +/** + * A {@link CallHandler} that sets the current scope on outgoing calls. + * @author Fabio Simeoni + * + */ +public class ScopeHandler extends AbstractHandler { + + /** 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); + + @Override + public void handleRequest(GCoreService target, SOAPHeader header, SOAPMessageContext context) 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); + } +} diff --git a/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/TargetServiceHandler.java b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/TargetServiceHandler.java new file mode 100644 index 0000000..167b37d --- /dev/null +++ b/src/main/java/org/gcube/common/clients/stubs/jaxws/handlers/TargetServiceHandler.java @@ -0,0 +1,33 @@ +package org.gcube.common.clients.stubs.jaxws.handlers; + +import javax.xml.namespace.QName; +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; + +/** + * A {@link CallHandler} that sets the coordinates of the target service on outgoing calls. + * + * @author Fabio Simeoni + * + */ +public class TargetServiceHandler extends AbstractHandler { + + /** Namespace of scope-related headers */ + private static final String SCOPE_NS = "http://gcube-system.org/namespaces/scope"; + + /** Name of the service class call header. */ + private static final String SERVICECLASS_HEADER_NAME = "serviceClass"; + private static final QName SERVICECLASS_QNAME = new QName(SCOPE_NS,SERVICECLASS_HEADER_NAME); + + /** Name of the service name call header. */ + private static final String SERVICENAME_HEADER_NAME = "serviceName"; + private static final QName SERVICENAME_QNAME = new QName(SCOPE_NS,SERVICENAME_HEADER_NAME); + + @Override + public void handleRequest(GCoreService target, SOAPHeader header, SOAPMessageContext context) throws Exception { + addHeader(header,SERVICECLASS_QNAME, target.gcubeClass()); + addHeader(header,SERVICENAME_QNAME, target.gcubeName()); + } +} diff --git a/src/main/resources/META-INF/services/org.gcube.common.clients.stubs.jaxws.handlers.CallHandler b/src/main/resources/META-INF/services/org.gcube.common.clients.stubs.jaxws.handlers.CallHandler new file mode 100644 index 0000000..34f8b70 --- /dev/null +++ b/src/main/resources/META-INF/services/org.gcube.common.clients.stubs.jaxws.handlers.CallHandler @@ -0,0 +1,4 @@ +org.gcube.common.clients.stubs.jaxws.handlers.ScopeHandler +org.gcube.common.clients.stubs.jaxws.handlers.TargetServiceHandler +org.gcube.common.clients.stubs.jaxws.handlers.ClientInfoHandler +org.gcube.common.clients.stubs.jaxws.handlers.LegacyWSAddressingHandler \ No newline at end of file diff --git a/src/test/java/org/acme/ConfigTest.java b/src/test/java/org/acme/ConfigTest.java new file mode 100644 index 0000000..9c5d366 --- /dev/null +++ b/src/test/java/org/acme/ConfigTest.java @@ -0,0 +1,55 @@ +package org.acme; + +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 org.acme.jaxws.stubs.StatelessStub; +import org.gcube.common.clients.stubs.jaxws.handlers.HandlerRegistry; +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; + +@RunWith(MyContainerTestRunner.class) +public class ConfigTest { + + @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() { + + ScopeProvider.instance.set("/gcube/devsec"); + + stub = stubFor(stateless).at(URI.create("http://localhost:9999/wsrf/services/acme/service/stateless")); + + } + + @Test + public void handlerTest() { + + TestHandler handler = new TestHandler(); + + assertTrue(HandlerRegistry.handlers().contains(handler)); + + String input = "input"; + String output = stub.foo(input); + + assertEquals(input, output); + + handler.called=true; + + assertTrue(HandlerRegistry.handlers().contains(handler)); + + } + + +} diff --git a/src/test/java/org/acme/TestHandler.java b/src/test/java/org/acme/TestHandler.java new file mode 100644 index 0000000..f6f8aa0 --- /dev/null +++ b/src/test/java/org/acme/TestHandler.java @@ -0,0 +1,41 @@ +package org.acme; + +import javax.xml.soap.SOAPHeader; +import javax.xml.ws.handler.soap.SOAPMessageContext; + +import org.gcube.common.clients.stubs.jaxws.GCoreService; +import org.gcube.common.clients.stubs.jaxws.handlers.AbstractHandler; + +public class TestHandler extends AbstractHandler { + + public boolean called=false; + + @Override + public void handleRequest(GCoreService target, SOAPHeader header, SOAPMessageContext context) throws Exception { + called=true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (called ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TestHandler other = (TestHandler) obj; + if (called != other.called) + return false; + return true; + } + + +} diff --git a/src/test/resources/META-INF/services/org.gcube.common.clients.stubs.jaxws.handlers.CallHandler b/src/test/resources/META-INF/services/org.gcube.common.clients.stubs.jaxws.handlers.CallHandler new file mode 100644 index 0000000..0b229d5 --- /dev/null +++ b/src/test/resources/META-INF/services/org.gcube.common.clients.stubs.jaxws.handlers.CallHandler @@ -0,0 +1 @@ +org.acme.TestHandler \ No newline at end of file