我在同一个端口上运行了SOAP Web服务和我的REST api。这是使用Spring 4.1.2 / CXF 2.7.15 / Jetty 8.1.16。现在我将我的版本升级到Spring 4.2.3 / CXF 3.1.4 / Jetty 9.3.6。似乎我可以在同一端口上安装SOAP Web服务或REST,具体取决于在启动期间首先在代码中添加的内容。我怎样才能让他们在同一时间再次工作?
更新
正在运行的代码示例:
import org.apache.cxf.endpoint.ServerImpl;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.http_jetty.JettyHTTPDestination;
import org.apache.cxf.transport.http_jetty.JettyHTTPServerEngine;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import my.package.app.DefaultApp;
import my.package.misc.jmx.JMXAgent;
import my.package.misc.xmpp.XmppManager;
import my.package.rest.standalone.JacksonJsonProvider;
import my.package.util.PropertyUtil;
import my.package.webservice.user.UserWs;
import javax.servlet.DispatcherType;
import javax.xml.ws.Endpoint;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class StartApp extends DefaultApp {
private static final Logger log = LoggerFactory.getLogger(StartApp.class);
private static boolean started;
// TODO: move this to SPRING
protected static ExecutorService startupExecutor = Executors.newFixedThreadPool(10);
// setting DNS caching to 1 hour
static {
java.security.Security.setProperty("networkaddress.cache.ttl", "3600");
}
private class ServiceStartTask implements Callable<Void> {
private Endpoint endpoint;
private String address;
private int port;
private String name;
private Class implementor;
private boolean startRest;
private String packages;
public ServiceStartTask(Endpoint endpoint, String address, int port, String name, Class implementor, boolean startRest, String packages) {
this.endpoint = endpoint;
this.address = address;
this.port = port;
this.name = name;
this.implementor = implementor;
this.startRest = startRest;
this.packages = packages;
log.debug("address = {} port = {} name = {} ", address, port, name);
}
@Override
public Void call() {
String baseUrl = "http://" + address + ":" + port + "/";
String wsUrl = baseUrl + name;
String restUrl = baseUrl + "rest";
log.info("Export ws on: " + wsUrl);
try {
endpoint = Endpoint.create(DefaultApp.getBean(implementor));
additionalJettySetup(endpoint, wsUrl, restUrl, implementor.getPackage().getName(), startRest, packages + ",my.package.rest.common");
} catch (Exception e) {
log.error("error publishing service " + wsUrl, e);
}
return null;
}
}
private Endpoint endpointUser;
public StartApp() {
}
public void start() {
String[] s = {};
start(s);
}
public void start(String[] args) {
if (started) {
return;
}
earlyInit();
long startTime = System.currentTimeMillis();
registerShutdownHook();
init();
if (PropertyUtil.getPropertyBoolean("jmx.enabled")) {
startJMX();
}
String address;
if (args.length == 0) {
address = "localhost";
} else {
address = args[0];
}
publishWebServices(address);
log.debug("starting rest services");
// separate jetty instance for additional rest services
int restPort = PropertyUtil.getPropertyInteger("rest.port");
startRest(address, restPort, "my.package.rest.standalone");
log.debug("rest services started");
started = true;
final long startupTime = System.currentTimeMillis() - startTime;
}
/**
* Setup all Jetty properties that cannot be configured in spring xml.
*
* @param endpoint
* @param restUrl
* @param domain
* @param startRest
* @throws IOException
*/
protected synchronized void additionalJettySetup(Endpoint endpoint, String wsUrl, String restUrl, String domain, boolean startRest, String packages) throws IOException {
if (endpoint instanceof EndpointImpl) {
// finding Jetty Server object
EndpointImpl endpointJetty = (EndpointImpl) endpoint;
// loging interceptors needs to be added before endpoint.getServer call
addInterceptors(endpointJetty);
ServerImpl server = endpointJetty.getServer(wsUrl);
if (endpointJetty.getServerFactory().getDestinationFactory().getDestination(server.getEndpoint().getEndpointInfo()) instanceof JettyHTTPDestination) {
JettyHTTPDestination d = (JettyHTTPDestination) endpointJetty.getServerFactory().getDestinationFactory().getDestination(server.getEndpoint().getEndpointInfo());
JettyHTTPServerEngine engine = (JettyHTTPServerEngine) d.getEngine();
if (startRest) {
log.info("Also starting REST service at {}", restUrl);
setRestConfig(engine, packages);
}
// publishing and starting the server
endpoint.publish(wsUrl);
// finally server is found
Server jetty = engine.getServer();
addJettyMBeans(domain, jetty);
}
}
}
private void addInterceptors(EndpointImpl endpointJetty) {
//adding some endpoint interceptors
}
/**
* Sets REST configuration for an existing server
*
* @param engine
* @param packages
*/
private void setRestConfig(final JettyHTTPServerEngine engine, final String packages) {
List<Handler> handlers = engine.getHandlers();
ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS);
setRestServletAndApplicationContext(root, packages, "/rest/*");
handlers.add(root);
}
/**
* Adds to JMX Jetty object that will be monitored.
*/
protected void addJettyMBeans(String domain, Server jetty) {
log.debug("creating MBeanContainer");
MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
mbContainer.setDomain(domain);
jetty.getContainer().addEventListener(mbContainer);
jetty.addBean(mbContainer);
// Adding bean that we want to be exposed to JMX
mbContainer.addBean(jetty.getConnectors()[0]);
mbContainer.addBean(jetty);
mbContainer.addBean(jetty.getThreadPool());
}
protected void startJMX() {
try {
JMXAgent.premain();
} catch (Throwable e) {
log.error("JMX agent startup error", e);
}
}
private void startRest(final String address, final int restPort, final String packages) {
startupExecutor.submit(new Runnable() {
@Override
public void run() {
startRestfullServer(address, restPort, packages);
}
});
}
private void startRestfullServer(final String address, final int restPort, final String packages) {
log.info("Starting REST service at port {}", restPort);
Server server = new Server(restPort);
try {
ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS);
setRestServletAndApplicationContext(root, packages + ",some.package", "/*");
server.setHandler(root);
server.setStopAtShutdown(true);
server.setGracefulShutdown(1000);
server.start();
server.join();
} catch (Exception e) {
log.error("error", e);
try {
server.stop();
} catch (Exception e1) {
log.error("error", e);
}
}
}
private void setRestServletAndApplicationContext(ServletContextHandler root, String packages, String servletPath) {
root.setContextPath("/");
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.packages("org.codehaus.jackson.jaxrs;" + packages);
// using our custom mapping provider
resourceConfig.register(JacksonJsonProvider.class);
// processing multipart
resourceConfig.register(MultiPartFeature.class);
resourceConfig.register(io.swagger.jaxrs.listing.ApiListingResource.class);
resourceConfig.register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
ServletContainer ss = new ServletContainer(resourceConfig);
ServletHolder servletHolder = new ServletHolder(ss);
root.addServlet(servletHolder, servletPath);
setUpSwagger();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.copyConfigurationFrom(getContext().getBeanFactory());
// CORS filter to allow swagger to be used from Swagger UI on another host
root.addFilter(CrossOriginFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST));
/**
* The trick is that Jersey expect WebApplication context and we need to mask our context as web context
* so we create a WebApplicationContext and makes methods that Jersey uses to return from spring context.
*/
final GenericWebApplicationContext webApplicationContext = new GenericWebApplicationContext(beanFactory) {
@Override
public String[] getBeanNamesForType(Class<?> type) {
return getParent().getBeanNamesForType(type);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return getParent().getBean(name, requiredType);
}
@Override
public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
return getParent().getBeansOfType(type);
}
};
webApplicationContext.setServletContext(root.getServletContext());
webApplicationContext.setParent(getContext());
root.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webApplicationContext);
webApplicationContext.refresh();
}
private void setUpSwagger() {
//settting up swagger
}
public void stop() {
log.info("Stopping ALL WSs");
getBean(XmppManager.class).send("Going off...!");
if (endpointUser != null) {
endpointUser.stop();
}
}
private void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
log.info("Shutdown hook for StartApp executed");
StartApp.this.stop();
}
});
}
private void publishWebServices(String address) {
int portUser = PropertyUtil.getPropertyInteger("webservice.user.port");
List<ServiceStartTask> services = new ArrayList<>();
services.add(publish(endpointUser, address, portUser, "user", UserWs.class, true, "rest.endpoint.package"));
for (ServiceStartTask task : services) {
task.call();
}
}
private <T> ServiceStartTask publish(Endpoint endpoint, String address, int port, String name, Class<T> implementor, boolean startRest, String packages) {
return new ServiceStartTask(endpoint, address, port, name, implementor, startRest, packages);
}
public static void main(String[] args) {
try {
new StartApp().start(args);
} catch (Exception e) {
log.error("Error starting app!", e);
System.exit(1);
}
}
}
在Spring中声明的Jetty引擎:
<!-- Jetty parameters -->
<httpj:engine-factory bus="cxf">
<!-- user -->
<httpj:engine port="${webservice.user.port}">
<httpj:threadingParameters minThreads="5"
maxThreads="100"/>
<httpj:connector>
<beans:bean class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<beans:property name="port" value="${webservice.user.port}"/>
<beans:property name="statsOn" value="false"/>
</beans:bean>
</httpj:connector>
<httpj:handlers>
<beans:bean class="org.eclipse.jetty.server.handler.DefaultHandler"/>
</httpj:handlers>
<httpj:sessionSupport>true</httpj:sessionSupport>
</httpj:engine>
</httpj:engine-factory>