From a8b411c16dc60b99a2e299468ef0f3f3d4cb9d05 Mon Sep 17 00:00:00 2001 From: Sergey Morgunov Date: Sat, 10 Aug 2019 00:14:56 +0300 Subject: [PATCH] #68 Add Play SOAP server --- .../http_play/PlayDestinationRegistry.java | 21 ++++++ .../PlayDestinationRegistryImpl.java | 58 +++++++++++++++++ .../http_play/PlayHTTPDestination.java | 30 +++++++++ .../http_play/PlayTransportFactory.java | 65 +++++++++++++++++++ .../play/soap/server/EndpointPublisher.java | 11 ++-- .../java/play/soap/server/SoapModule.java | 29 ++++++++- .../resources/META-INF/cxf/bus-extensions.txt | 1 + .../play/soap/server/SoapController.scala | 19 ++++++ .../scala/play/soap/server/SoapRouter.scala | 17 +++++ .../src/test/resources/logback-test.xml | 12 ++++ .../play/soap/server/SoapServerSpec.scala | 9 +-- 11 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistry.java create mode 100644 play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistryImpl.java create mode 100644 play-server/src/main/java/org/apache/cxf/transport/http_play/PlayHTTPDestination.java create mode 100644 play-server/src/main/java/org/apache/cxf/transport/http_play/PlayTransportFactory.java create mode 100644 play-server/src/main/resources/META-INF/cxf/bus-extensions.txt create mode 100644 play-server/src/main/scala/play/soap/server/SoapController.scala create mode 100644 play-server/src/main/scala/play/soap/server/SoapRouter.scala create mode 100644 play-server/src/test/resources/logback-test.xml diff --git a/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistry.java b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistry.java new file mode 100644 index 00000000..83d111b8 --- /dev/null +++ b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistry.java @@ -0,0 +1,21 @@ +package org.apache.cxf.transport.http_play; + +/** Inspired by {@link org.apache.cxf.transport.http.DestinationRegistry} */ +public interface PlayDestinationRegistry { + + PlayHTTPDestination addDestination(PlayHTTPDestination destination); + + // void removeDestination(String path); + + PlayHTTPDestination getDestinationForPath(String path); + + // PlayHTTPDestination getDestinationForPath(String path, boolean tryDecoding); + // + // PlayHTTPDestination checkRestfulRequest(String address); + // + // Collection getDestinations(); + // + // AbstractDestination[] getSortedDestinations(); + // + // Set getDestinationsPaths(); +} diff --git a/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistryImpl.java b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistryImpl.java new file mode 100644 index 00000000..33622409 --- /dev/null +++ b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayDestinationRegistryImpl.java @@ -0,0 +1,58 @@ +package org.apache.cxf.transport.http_play; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** Inpired by {@link org.apache.cxf.transport.http.DestinationRegistryImpl} */ +public class PlayDestinationRegistryImpl implements PlayDestinationRegistry { + + private ConcurrentMap destinations = new ConcurrentHashMap<>(); + private ConcurrentMap decodedDestinations = + new ConcurrentHashMap<>(); + + public PlayHTTPDestination addDestination(PlayHTTPDestination destination) { + String path = getTrimmedPath(destination.getEndpointInfo().getAddress()); + PlayHTTPDestination dest = destinations.computeIfAbsent(path, key -> destination); + try { + String path2 = URLDecoder.decode(path, "UTF-8"); + if (!path.equals(path2)) { + decodedDestinations.putIfAbsent(path2, dest); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Unsupported Encoding", e); + } + return dest; + } + + public PlayHTTPDestination getDestinationForPath(String path) { + String m = getTrimmedPath(path); + PlayHTTPDestination s = destinations.get(m); + return (s != null) ? s : decodedDestinations.get(m); + } + + /** + * Remove the transport protocol from the path and make it starts with / + * + * @param path + * @return trimmed path + */ + private String getTrimmedPath(String path) { + if (path == null) { + return "/"; + } + final String lh = "http://localhost/"; + final String lhs = "https://localhost/"; + + if (path.startsWith(lh)) { + path = path.substring(lh.length()); + } else if (path.startsWith(lhs)) { + path = path.substring(lhs.length()); + } + if (!path.contains("://") && !path.startsWith("/")) { + path = "/" + path; + } + return path; + } +} diff --git a/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayHTTPDestination.java b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayHTTPDestination.java new file mode 100644 index 00000000..d559d225 --- /dev/null +++ b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayHTTPDestination.java @@ -0,0 +1,30 @@ +package org.apache.cxf.transport.http_play; + +import org.apache.cxf.Bus; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.service.model.EndpointInfo; +import org.apache.cxf.transport.AbstractDestination; +import org.apache.cxf.transport.Conduit; + +import java.util.logging.Logger; + +/** Play HTTP destination */ +public class PlayHTTPDestination extends AbstractDestination { + + private static final Logger LOG = LogUtils.getL7dLogger(PlayHTTPDestination.class); + + public PlayHTTPDestination(Bus b, EndpointInfo ei) { + super(b, getTargetReference(ei, b), ei); + } + + @Override + protected Conduit getInbuiltBackChannel(Message inMessage) { + return null; + } + + @Override + protected Logger getLogger() { + return LOG; + } +} diff --git a/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayTransportFactory.java b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayTransportFactory.java new file mode 100644 index 00000000..59a3d171 --- /dev/null +++ b/play-server/src/main/java/org/apache/cxf/transport/http_play/PlayTransportFactory.java @@ -0,0 +1,65 @@ +package org.apache.cxf.transport.http_play; + +import org.apache.cxf.Bus; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.service.model.EndpointInfo; +import org.apache.cxf.transport.AbstractTransportFactory; +import org.apache.cxf.transport.Conduit; +import org.apache.cxf.transport.ConduitInitiator; +import org.apache.cxf.transport.Destination; +import org.apache.cxf.transport.DestinationFactory; +import org.apache.cxf.ws.addressing.EndpointReferenceType; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +/** Inspired by {@link org.apache.cxf.transport.http.HTTPTransportFactory} */ +public class PlayTransportFactory extends AbstractTransportFactory + implements ConduitInitiator, DestinationFactory { + + // public static final List DEFAULT_NAMESPACES = + // Collections.unmodifiableList( + // Arrays.asList( + // "http://cxf.apache.org/transports/http", + // "http://cxf.apache.org/transports/http/configuration", + // "http://schemas.xmlsoap.org/wsdl/http", + // "http://schemas.xmlsoap.org/wsdl/http/")); + + public static final List DEFAULT_NAMESPACES = + Collections.unmodifiableList(Arrays.asList("http://cxf.apache.org/transports/play")); + + private static final Logger LOG = LogUtils.getL7dLogger(PlayTransportFactory.class); + + protected final PlayDestinationRegistry registry; + + public PlayTransportFactory() { + super(DEFAULT_NAMESPACES); + this.registry = new PlayDestinationRegistryImpl(); + } + + @Override + public Conduit getConduit(EndpointInfo targetInfo, Bus bus) throws IOException { + return null; + } + + @Override + public Conduit getConduit(EndpointInfo localInfo, EndpointReferenceType target, Bus bus) + throws IOException { + return null; + } + + @Override + public Destination getDestination(EndpointInfo endpointInfo, Bus bus) throws IOException { + if (endpointInfo == null) throw new IllegalArgumentException("EndpointInfo cannot be null"); + PlayHTTPDestination dest = registry.getDestinationForPath(endpointInfo.getAddress()); + if (dest == null) dest = registry.addDestination(createDestination(endpointInfo, bus)); + return dest; + } + + private PlayHTTPDestination createDestination(EndpointInfo endpointInfo, Bus bus) { + return new PlayHTTPDestination(bus, endpointInfo); + } +} diff --git a/play-server/src/main/java/play/soap/server/EndpointPublisher.java b/play-server/src/main/java/play/soap/server/EndpointPublisher.java index 0863fb47..b2f1d7cd 100644 --- a/play-server/src/main/java/play/soap/server/EndpointPublisher.java +++ b/play-server/src/main/java/play/soap/server/EndpointPublisher.java @@ -4,7 +4,7 @@ package play.soap.server; import com.typesafe.config.Config; -import org.apache.cxf.BusFactory; +import org.apache.cxf.Bus; import org.apache.cxf.jaxws.EndpointImpl; import org.apache.cxf.jaxws.JaxWsServerFactoryBean; import org.slf4j.Logger; @@ -31,6 +31,7 @@ public class EndpointPublisher { public EndpointPublisher( ApplicationLifecycle lifecycle, JaxWsServerFactoryBean server, + Bus bus, Injector injector, Config config) throws ClassNotFoundException { @@ -38,11 +39,9 @@ public EndpointPublisher( for (Config endpointConfig : configEndpoints) { String address = endpointConfig.getString("address"); String implementorClass = endpointConfig.getString("implementor"); - Object implementor = - injector.instanceOf( - Thread.currentThread().getContextClassLoader().loadClass(implementorClass)); - EndpointImpl endpoint = - new EndpointImpl(BusFactory.getThreadDefaultBus(), implementor, server); + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + Object implementor = injector.instanceOf(contextClassLoader.loadClass(implementorClass)); + EndpointImpl endpoint = new EndpointImpl(bus, implementor, server); endpoint.setAddress(address); endpoints.add(endpoint); } diff --git a/play-server/src/main/java/play/soap/server/SoapModule.java b/play-server/src/main/java/play/soap/server/SoapModule.java index 565cc201..1e8c5f63 100644 --- a/play-server/src/main/java/play/soap/server/SoapModule.java +++ b/play-server/src/main/java/play/soap/server/SoapModule.java @@ -4,25 +4,52 @@ package play.soap.server; import com.typesafe.config.Config; +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; import org.apache.cxf.jaxws.JaxWsServerFactoryBean; import org.apache.cxf.jaxws.PlayJaxWsServerFactoryBean; import org.apache.cxf.jaxws.PlayJaxWsServiceFactoryBean; import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean; +import org.apache.cxf.transport.http_play.PlayTransportFactory; import play.Environment; import play.inject.Binding; import play.inject.Module; import java.util.Arrays; import java.util.List; +import javax.inject.Provider; +import javax.inject.Singleton; -/** @author Sergey Morgunov {@literal } */ +/** Guice module for Play Soap Server */ public class SoapModule extends Module { + @Singleton + public static class BusProvider implements Provider { + + private final Bus bus = BusFactory.newInstance().createBus(); + + // private final Provider transportFactoryProvider; + // + // @Inject + // public BusProvider(Provider transportFactoryProvider) { + // this.transportFactoryProvider = transportFactoryProvider; + // } + + @Override + public Bus get() { + // Bus bus = factory.createBus(); + // bus.setExtension(transportFactoryProvider.get(), PlayTransportFactory.class); + return bus; + } + } + @Override public List> bindings(Environment environment, Config config) { return Arrays.asList( bindClass(JaxWsServiceFactoryBean.class).to(PlayJaxWsServiceFactoryBean.class), bindClass(JaxWsServerFactoryBean.class).to(PlayJaxWsServerFactoryBean.class), + bindClass(PlayTransportFactory.class).toSelf(), + bindClass(Bus.class).toProvider(BusProvider.class), bindClass(EndpointPublisher.class).toSelf().eagerly()); } } diff --git a/play-server/src/main/resources/META-INF/cxf/bus-extensions.txt b/play-server/src/main/resources/META-INF/cxf/bus-extensions.txt new file mode 100644 index 00000000..764983c5 --- /dev/null +++ b/play-server/src/main/resources/META-INF/cxf/bus-extensions.txt @@ -0,0 +1 @@ +org.apache.cxf.transport.http_play.PlayTransportFactory::true \ No newline at end of file diff --git a/play-server/src/main/scala/play/soap/server/SoapController.scala b/play-server/src/main/scala/play/soap/server/SoapController.scala new file mode 100644 index 00000000..0ed2c32e --- /dev/null +++ b/play-server/src/main/scala/play/soap/server/SoapController.scala @@ -0,0 +1,19 @@ +package play.soap.server + +import javax.inject.Inject +import org.apache.cxf.Bus +import org.apache.cxf.bus.extension.ExtensionManager +import org.apache.cxf.transport.http_play.PlayTransportFactory +import play.api.mvc.{AbstractController, ControllerComponents} + +class SoapController @Inject()(cc: ControllerComponents, bus: Bus) extends AbstractController(cc) { + + val transportFactory: PlayTransportFactory = { + val extensionManager = bus.getExtension(classOf[ExtensionManager]) + extensionManager.getExtension(classOf[PlayTransportFactory].getName, classOf[PlayTransportFactory]) + } + + def echo = Action { request => + Ok("Got request [" + request + "]") + } +} diff --git a/play-server/src/main/scala/play/soap/server/SoapRouter.scala b/play-server/src/main/scala/play/soap/server/SoapRouter.scala new file mode 100644 index 00000000..ac9214a1 --- /dev/null +++ b/play-server/src/main/scala/play/soap/server/SoapRouter.scala @@ -0,0 +1,17 @@ +package play.soap.server + +import javax.inject.Inject +import play.api.routing.Router.Routes +import play.api.routing.SimpleRouter +import play.api.routing.sird._ + +class SoapRouter @Inject()(controller: SoapController) extends SimpleRouter { + override def routes: Routes = { + case GET(p"/$path*") => { + controller.echo() + } + case POST(p"/*$path*") => { + controller.echo() + } + } +} \ No newline at end of file diff --git a/play-server/src/test/resources/logback-test.xml b/play-server/src/test/resources/logback-test.xml new file mode 100644 index 00000000..e307cf44 --- /dev/null +++ b/play-server/src/test/resources/logback-test.xml @@ -0,0 +1,12 @@ + + + + + %date{"yyyy-MM-dd'T'HH:mm:ss.SSSZ"} %level %logger [%mdc] - %msg%n + + + + + + + \ No newline at end of file diff --git a/play-server/src/test/scala/play/soap/server/SoapServerSpec.scala b/play-server/src/test/scala/play/soap/server/SoapServerSpec.scala index 02c3598d..3bdf0b15 100644 --- a/play-server/src/test/scala/play/soap/server/SoapServerSpec.scala +++ b/play-server/src/test/scala/play/soap/server/SoapServerSpec.scala @@ -10,6 +10,7 @@ import org.specs2.specification.BeforeAll import play.api.Configuration import play.api.inject.bind import play.api.inject.guice.GuiceApplicationBuilder +import play.api.routing.Router import play.api.test.WithServer import play.inject.ApplicationLifecycle import play.inject.DelegateApplicationLifecycle @@ -20,19 +21,19 @@ import play.soap.testservice.client.User import scala.collection.JavaConverters._ -class SoapServerSpec extends Specification with BeforeAll { +class SoapServerSpec extends Specification { sequential - override def beforeAll(): Unit = {} - "SOAP server" in { "with Java Futures implementation" in new WithServer( app = GuiceApplicationBuilder() .configure(new Configuration(ConfigFactory.parseResources("application.java_server.conf"))) .bindings(bind[ApplicationLifecycle].to[DelegateApplicationLifecycle]) - .build() + .overrides(bind[Router].to[SoapRouter]) + .build(), + port = 9005 ) { withJavaClient { client => testHelloService(client)