You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2015/07/04 13:53:56 UTC

isis git commit: ISIS-1166: further improvements/simplifications to SoapEndpointPublishingRule

Repository: isis
Updated Branches:
  refs/heads/master faf5c6656 -> 1d27060fa


ISIS-1166: further improvements/simplifications to SoapEndpointPublishingRule


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/1d27060f
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/1d27060f
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/1d27060f

Branch: refs/heads/master
Commit: 1d27060fa6cae298e4cbb6227279af239373d136
Parents: faf5c66
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Sat Jul 4 12:53:39 2015 +0100
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Sat Jul 4 12:53:39 2015 +0100

----------------------------------------------------------------------
 ...est-support_soap-fake-server-junit-rule.adoc | 84 +++++++----------
 .../soap/PublishedEndpoints.java                | 54 +++++++++++
 .../core/unittestsupport/soap/SoapEndpoint.java | 55 ++++++++++++
 .../soap/SoapEndpointPublishingRule.java        | 94 ++++----------------
 .../unittestsupport/soap/SoapEndpointSpec.java  | 92 +++++++++++++++++++
 5 files changed, 253 insertions(+), 126 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/1d27060f/adocs/documentation/src/main/asciidoc/guides/_ug_testing_unit-test-support_soap-fake-server-junit-rule.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_ug_testing_unit-test-support_soap-fake-server-junit-rule.adoc b/adocs/documentation/src/main/asciidoc/guides/_ug_testing_unit-test-support_soap-fake-server-junit-rule.adoc
index 3d1f314..b173be2 100644
--- a/adocs/documentation/src/main/asciidoc/guides/_ug_testing_unit-test-support_soap-fake-server-junit-rule.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/_ug_testing_unit-test-support_soap-fake-server-junit-rule.adoc
@@ -28,40 +28,24 @@ In the test methods your unit test sets up expectations on your fake server, and
 
 So that tests don't take too long to run, the rule puts the fake server endpoints onto a thread-local.  Therefore the unit tests should clear up any state on the fake server endpoints.
 
-The easiest way to use the rule is to subclass it, eg:
-
-[source,java]
-----
-public class FakeExternalSystemEndpointRule extends SoapEndpointPublishingRule {
-    public FakeExternalSystemEndpointRule() {
-        super(
-            FakeExternalSystemEndpoint.class,                   // <1>
-            "http://localhost:54345/any/old/string/will/work",  // <2>
-            );
-    }
-}
-----
-<1> specify the class that implements the endpoint (must have a no-arg constructor)
-<2> provide an address to host the endpoint.  The port can be hard-coded, as shown, or (probably better) could choose one at random from within a range
-
-
-Your unit test should then look something like:
+Your unit test uses the rule by specifying the endpoint class (must have a no-arg constructor):
 
 [source,java]
 ----
 public class FakeExternalSystemEndpointRuleTest {
     @Rule
-    public FakeExternalSystemEndpointRule serverRule = new FakeExternalSystemEndpointRule();
+    public SoapEndpointPublishingRule serverRule =
+        new SoapEndpointPublishingRule(FakeExternalSystemEndpoint.class);         // <1>
     private FakeExternalSystemEndpoint fakeServerEndpoint;
-    private DemoObject externalSystemContract;                                      // <1>
+    private DemoObject externalSystemContract;                                    // <2>
     @Before
     public void setUp() throws Exception {
         fakeServerEndpoint =
-            serverRule.getEndpointImplementor(ExternalSystemFakeServer.class);      // <2>
+            serverRule.getEndpointImplementor(FakeExternalSystemEndpoint.class);  // <3>
         final String endpointAddress =
-            serverRule.getEndpointAddress(ExternalSystemFakeServer.class);          // <3>
-        final DemoObjectService externalSystemService =                             // <4>
-                new DemoObjectService(ExternalSystemWsdl.getWsdl());                // <5>
+            serverRule.getEndpointAddress(FakeExternalSystemEndpoint.class);      // <4>
+        final DemoObjectService externalSystemService =                           // <5>
+                new DemoObjectService(ExternalSystemWsdl.getWsdl());              // <6>
         externalSystemContract = externalSystemService.getDemoObjectOverSOAP();
         BindingProvider provider = (BindingProvider) externalSystemContract;
         provider.getRequestContext().put(
@@ -72,51 +56,46 @@ public class FakeExternalSystemEndpointRuleTest {
     @Test
     public void happy_case() throws Exception {
         // given
-        final Update update = new Update();                                         // <6>
+        final Update update = new Update();                              // <7>
         ...
         // expect
-        final UpdateResponse response = new UpdateResponse();                       // <7>
+        final UpdateResponse response = new UpdateResponse();            // <8>
         ...
         fakeServerEndpoint.control().setResponse(updateResponse);
         // when
-        PostResponse response = externalSystemContract.post(update);                // <8>
+        PostResponse response = externalSystemContract.post(update);     // <9>
         // then
-        final List<Update> updates =                                                // <9>
+        final List<Update> updates =                                     // <10>
             fakeServerEndpoint.control().getUpdates();
         ...
     }
 }
 ----
-<1> the SOAP contract as defined in WSDL and generated by wsdl2java
-<2> get hold of the fake server-side endpoint from the rule...
-<3> ... and its endpoint address
-<4> use factory (also geneated by wsdl2java) to create client-side endpoint
-<5> `getWsdl()` is a utility method to return a URL for the WSDL (eg from the classpath)
-<6> create a request (generated from the WSDL and wsdl2java)
-<7> instruct fake server how to respond
-<8> invoke the service
-<9> check service was correctly invoked etc.
+<1> specify the class that implements the endpoint (must have a no-arg constructor)
+<2> the SOAP contract as defined in WSDL and generated by wsdl2java
+<3> get hold of the fake server-side endpoint from the rule...
+<4> ... and its endpoint address
+<5> use factory (also generated by wsdl2java) to create client-side endpoint
+<6> `getWsdl()` is a utility method to return a URL for the WSDL (eg from the classpath)
+<7> create a request object in order to invoke the SOAP web service
+<8> instruct the fake server endpoint how to respond
+<9> invoke the web service
+<10> check the fake server endpoint was correctly invoked etc.
 
-For "real-world" usage, see the example app of the (non-ASF) http://github.com/isisaddons/isis-module-publishmq[Isis addons' publishmq] module.
 
-Alternatively, the rule can host multiple endpoints.  Each of these will be at a separate address.
+The rule can also host multiple endpoints; just provide multiple classes in the constructor:
 
 [source,java]
 ----
-public class ExternalSystemFakeServerRule extends SoapEndpointPublishingRule {
-    public ExternalSystemFakeServerRule() {
-        super(
-            new SoapEndpointSpec(FakeCustomersEndpoint.class,
-                                 "http://localhost:54346/customers"),
-            new SoapEndpointSpec(FakeOrdersEndpoint.class,
-                                 "http://localhost:54347/orders"),
-            new SoapEndpointSpec(FakeProductsEndpoint.class,
-                                 "http://localhost:54348/products"))
-    }
-}
+@Rule
+public SoapEndpointPublishingRule serverRule =
+                new SoapEndpointPublishingRule(
+                    FakeCustomersEndpoint.class,
+                    FakeOrdersEndpoint.class,
+                    FakeProductsEndpoint.class);
 ----
 
-To lookup a particular endpoint, specify its type, eg:
+To lookup a particular endpoint, specify its type:
 
 [source,java]
 ----
@@ -124,6 +103,9 @@ FakeProductsEndpoint fakeProductsServerEndpoint =
             serverRule.getPublishedEndpoint(FakeProductsEndpoint.class);
 ----
 
+The endpoint addresses that the server endpoints run on are determined automatically.  If you want more control, then you can call one of ``SoapEndpointPublishingRule``'s overloaded constructors, passing in one or more `SoapEndpointSpec` instances.
+
+
 
 
 == XML Marshalling Support

http://git-wip-us.apache.org/repos/asf/isis/blob/1d27060f/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/PublishedEndpoints.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/PublishedEndpoints.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/PublishedEndpoints.java
new file mode 100644
index 0000000..78a340a
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/PublishedEndpoints.java
@@ -0,0 +1,54 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.soap;
+
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+/**
+ * Collection of SOAP endpoints that have been published; will automatically assign a unique address to any
+ * that have not been {@link SoapEndpoint}s whose {@link SoapEndpointSpec} does not specify an {@link SoapEndpointSpec#getEndpointAddress() address}.
+ */
+class PublishedEndpoints {
+
+    private int port = SoapEndpointPublishingRule.INITIAL_PORT;
+    private Map<Class<?>, SoapEndpoint> soapEndpointByType = Maps.newLinkedHashMap();
+
+    void publishEndpointIfRequired(final List<SoapEndpointSpec> soapEndpointSpecs) {
+        // merge in any new endpoints to static cache
+        for (SoapEndpointSpec soapEndpointSpec : soapEndpointSpecs) {
+            final Class<?> endpointClass = soapEndpointSpec.getEndpointClass();
+            SoapEndpoint soapEndpoint = this.soapEndpointByType.get(endpointClass);
+            if (soapEndpoint == null) {
+                // instantiate and publish,automatically assigning an address to any that don't specify one
+                soapEndpoint = new SoapEndpoint(soapEndpointSpec);
+                soapEndpointByType.put(endpointClass, soapEndpoint);
+                port = soapEndpoint.publish(port);
+            }
+        }
+    }
+
+    String getEndpointAddress(Class<?> endpointClass) {
+        return soapEndpointByType.get(endpointClass).getSpec().getEndpointAddress();
+    }
+
+    <T> T getEndpointImplementor(Class<T> endpointClass) {
+        return (T) soapEndpointByType.get(endpointClass).getImplementor();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/1d27060f/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpoint.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpoint.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpoint.java
new file mode 100644
index 0000000..bc38c65
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpoint.java
@@ -0,0 +1,55 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.soap;
+
+import javax.xml.ws.Endpoint;
+
+class SoapEndpoint {
+
+    private final SoapEndpointSpec spec;
+    SoapEndpointSpec getSpec() {
+        return spec;
+    }
+
+    SoapEndpoint(final SoapEndpointSpec spec) {
+        this.spec = spec;
+    }
+
+    private Object implementor;
+    /**
+     * populated when {@link #publish(int) publish}ed.
+     */
+    public Object getImplementor() {
+        return implementor;
+    }
+
+    int publish(int port) {
+        if (implementor == null) {
+            this.implementor = getSpec().getEndpointImplementorFactory().get();
+
+            String endpointAddress = getSpec().getEndpointAddress();
+            if (endpointAddress == null) {
+                endpointAddress = String.format("http://localhost:%d/%s", port++, getSpec().getEndpointClass().getName().replace(".", "/"));
+                getSpec().setEndpointAddress(endpointAddress);
+            }
+            Endpoint.publish(endpointAddress, getImplementor());
+        }
+        return port;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/1d27060f/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointPublishingRule.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointPublishingRule.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointPublishingRule.java
index 4eba9e5..71fd61b 100644
--- a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointPublishingRule.java
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointPublishingRule.java
@@ -18,12 +18,9 @@ package org.apache.isis.core.unittestsupport.soap;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
 
-import javax.xml.ws.Endpoint;
-
-import com.google.common.collect.Maps;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -35,101 +32,48 @@ import org.junit.runners.model.Statement;
  */
 public class SoapEndpointPublishingRule implements TestRule {
 
-    private static Map<Class<?>,SoapEndpoint> cachedSoapEndpointByType = Maps.newLinkedHashMap();
+    /**
+     * For any endpoints where the address is not specified, ports are assigned starting from this.
+     */
+    static final int INITIAL_PORT = 54345;
 
-    public static class SoapEndpointSpec {
-        private final Class<?> endpointClass;
-        private final String endpointAddress;
-        private final Supplier<?> endpointImplementorFactory;
+    private static PublishedEndpoints publishedEndpoints = new PublishedEndpoints();
 
-        public SoapEndpointSpec(final Class<?> endpointClass, final String endpointAddress) {
-            this(endpointClass, endpointAddress, new SupplierUsingDefaultConstructor(endpointClass));
-        }
-        public SoapEndpointSpec(final Class<?> endpointClass, final String endpointAddress, final Supplier<?> endpointImplementorFactory) {
-            this.endpointClass = endpointClass;
-            this.endpointAddress = endpointAddress;
-            this.endpointImplementorFactory = endpointImplementorFactory;
-        }
+    private final List<SoapEndpointSpec> soapEndpointSpecs = Lists.newArrayList();
 
+    public SoapEndpointPublishingRule(final Class<?> endpointClass, final String endpointAddress) {
+        this(new SoapEndpointSpec(endpointClass, endpointAddress));
     }
 
-    private static class SoapEndpoint {
-        private final SoapEndpointSpec spec;
-        /**
-         * lazily populated first time
-         */
-        private Object implementor;
-        public SoapEndpoint(final SoapEndpointSpec spec) {
-            this.spec = spec;
-        }
+    public SoapEndpointPublishingRule(Class<?>... endpointClasses) {
+        this(Arrays.asList(endpointClasses));
     }
 
-    private final Map<Class<?>,SoapEndpoint> soapEndpointByType = Maps.newLinkedHashMap();
-
-    public SoapEndpointPublishingRule(final Class<?> endpointClass, final String endpointAddress) {
-        this(new SoapEndpointSpec(endpointClass, endpointAddress, new SupplierUsingDefaultConstructor(endpointClass)));
+    public SoapEndpointPublishingRule(final List<Class<?>> endpointClasses) {
+        this(Iterables.transform(endpointClasses, SoapEndpointSpec.asSoapEndpointSpec()));
     }
 
     public SoapEndpointPublishingRule(SoapEndpointSpec... soapEndpointSpecs) {
         this(Arrays.asList(soapEndpointSpecs));
     }
 
-    public SoapEndpointPublishingRule(final List<SoapEndpointSpec> soapEndpointSpecs) {
-        for (SoapEndpointSpec soapEndpointSpec : soapEndpointSpecs) {
-            soapEndpointByType.put(soapEndpointSpec.endpointClass, new SoapEndpoint(soapEndpointSpec));
-        }
+    public SoapEndpointPublishingRule(final Iterable<SoapEndpointSpec> soapEndpointSpecs) {
+        Iterables.addAll(this.soapEndpointSpecs, soapEndpointSpecs);
     }
 
     @Override
     public Statement apply(final Statement base, final Description description) {
-        publishEndpointIfRequired();
+        publishedEndpoints.publishEndpointIfRequired(soapEndpointSpecs);
         return base;
     }
 
-    private void publishEndpointIfRequired() {
-        // merge in any new endpoints to static cache
-        for (Class<?> type : soapEndpointByType.keySet()) {
-            final SoapEndpoint soapEndpoint = soapEndpointByType.get(type);
-            final Class<?> endpointClass = soapEndpoint.spec.endpointClass;
-            final SoapEndpoint cachedSoapEndpoint = cachedSoapEndpointByType.get(endpointClass);
-            if(cachedSoapEndpoint == null) {
-                cachedSoapEndpointByType.put(endpointClass, soapEndpoint);
-            }
-        }
-        // ensure all endpoints (including any new ones) are instantiated and published
-        for (Class<?> type : cachedSoapEndpointByType.keySet()) {
-            final SoapEndpoint cachedSoapEndpoint = cachedSoapEndpointByType.get(type);
-            if(cachedSoapEndpoint.implementor == null) {
-                cachedSoapEndpoint.implementor = cachedSoapEndpoint.spec.endpointImplementorFactory.get();
-                Endpoint.publish(cachedSoapEndpoint.spec.endpointAddress, cachedSoapEndpoint.implementor);
-            }
-        }
-    }
-
     public String getEndpointAddress(Class<?> endpointClass) {
-        return cachedSoapEndpointByType.get(endpointClass).spec.endpointAddress;
+        return publishedEndpoints.getEndpointAddress(endpointClass);
     }
 
     public <T> T getEndpointImplementor(Class<T> endpointClass) {
-        return (T)cachedSoapEndpointByType.get(endpointClass).implementor;
+        return publishedEndpoints.getEndpointImplementor(endpointClass);
     }
 
 
-    private static class SupplierUsingDefaultConstructor implements Supplier<Object> {
-        private final Class<?> endpointClass;
-
-        public SupplierUsingDefaultConstructor(final Class<?> endpointClass) {
-            this.endpointClass = endpointClass;
-        }
-
-        @Override
-        public Object get() {
-            try {
-                return endpointClass.newInstance();
-            } catch (InstantiationException | IllegalAccessException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/1d27060f/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointSpec.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointSpec.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointSpec.java
new file mode 100644
index 0000000..e3511e6
--- /dev/null
+++ b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/soap/SoapEndpointSpec.java
@@ -0,0 +1,92 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.soap;
+
+import java.util.function.Supplier;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+
+public class SoapEndpointSpec {
+
+    static Function<Class<?>, SoapEndpointSpec> asSoapEndpointSpec() {
+        return new Function<Class<?>, SoapEndpointSpec>() {
+            @Nullable @Override
+            public SoapEndpointSpec apply(final Class<?> input) {
+                return new SoapEndpointSpec(input);
+            }
+        };
+    }
+
+    public SoapEndpointSpec(final Class<?> endpointClass) {
+        this(endpointClass, new SupplierUsingDefaultConstructor(endpointClass), null);
+    }
+
+    public SoapEndpointSpec(final Class<?> endpointClass, final String endpointAddress) {
+        this(endpointClass, new SupplierUsingDefaultConstructor(endpointClass), endpointAddress);
+    }
+
+    public SoapEndpointSpec(final Class<?> endpointClass, final Supplier<?> endpointImplementorFactory) {
+        this(endpointClass, endpointImplementorFactory, null);
+    }
+
+    public SoapEndpointSpec(final Class<?> endpointClass, final Supplier<?> endpointImplementorFactory, final String endpointAddress) {
+        this.endpointClass = endpointClass;
+        this.endpointAddress = endpointAddress;
+        this.endpointImplementorFactory = endpointImplementorFactory;
+    }
+
+    private final Class<?> endpointClass;
+    Class<?> getEndpointClass() {
+        return endpointClass;
+    }
+
+    private final Supplier<?> endpointImplementorFactory;
+    Supplier<?> getEndpointImplementorFactory() {
+        return endpointImplementorFactory;
+    }
+
+    private String endpointAddress;
+    String getEndpointAddress() {
+        return endpointAddress;
+    }
+    /**
+     * Populated when published if not otherwise.
+     */
+    void setEndpointAddress(String endpointAddress) {
+        this.endpointAddress = endpointAddress;
+    }
+
+    private static class SupplierUsingDefaultConstructor implements Supplier<Object> {
+        private final Class<?> endpointClass;
+
+        public SupplierUsingDefaultConstructor(final Class<?> endpointClass) {
+            this.endpointClass = endpointClass;
+        }
+
+        @Override
+        public Object get() {
+            try {
+                return endpointClass.newInstance();
+            } catch (InstantiationException | IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+}