blob: f47cdc275c422b3070ff5c00e8b08295cb195d76 [file] [log] [blame]
/*
* 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.dubbo.rpc.protocol.webservice;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.RemotingServer;
import org.apache.dubbo.remoting.http.HttpBinder;
import org.apache.dubbo.remoting.http.HttpHandler;
import org.apache.dubbo.remoting.http.servlet.DispatcherServlet;
import org.apache.dubbo.rpc.ProtocolServer;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol;
import org.apache.cxf.binding.soap.SoapTransportFactory;
import org.apache.cxf.bus.extension.ExtensionManagerBus;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.frontend.ClientProxyFactoryBean;
import org.apache.cxf.frontend.ServerFactoryBean;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.service.model.OperationInfo;
import org.apache.cxf.transport.Destination;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.apache.cxf.transport.http.DestinationRegistry;
import org.apache.cxf.transport.http.DestinationRegistryImpl;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.HttpDestinationFactory;
import org.apache.cxf.transport.servlet.ServletController;
import org.apache.cxf.transport.servlet.ServletDestinationFactory;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.cxf.wsdl.service.factory.AbstractServiceConfiguration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_SERVER;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_SERVER_SERVLET;
import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_PATH_PREFIX;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
/**
* WebServiceProtocol.
*/
public class WebServiceProtocol extends AbstractProxyProtocol {
public static final int DEFAULT_PORT = 80;
private final ExtensionManagerBus bus = new ExtensionManagerBus();
private SoapTransportFactory transportFactory = null;
private ServerFactoryBean serverFactoryBean = null;
private DestinationRegistry destinationRegistry = null;
private HttpBinder httpBinder;
private Server server = null;
public WebServiceProtocol() {
super(Fault.class);
bus.setExtension(new ServletDestinationFactory(), HttpDestinationFactory.class);
}
public void setHttpBinder(HttpBinder httpBinder) {
this.httpBinder = httpBinder;
}
@Override
public int getDefaultPort() {
return DEFAULT_PORT;
}
@Override
protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
transportFactory = new SoapTransportFactory();
destinationRegistry = new DestinationRegistryImpl();
String addr = getAddr(url);
ProtocolServer protocolServer = serverMap.get(addr);
if (protocolServer == null) {
RemotingServer remotingServer = httpBinder.bind(url, new WebServiceHandler());
serverMap.put(addr, new ProxyProtocolServer(remotingServer));
}
serverFactoryBean = new ServerFactoryBean();
serverFactoryBean.setAddress(url.getAbsolutePath());
serverFactoryBean.setServiceClass(type);
serverFactoryBean.setServiceBean(impl);
serverFactoryBean.setBus(bus);
serverFactoryBean.setDestinationFactory(transportFactory);
serverFactoryBean.getServiceFactory().getConfigurations().add(new URLHashMethodNameSoapActionServiceConfiguration());
server = serverFactoryBean.create();
return new Runnable() {
@Override
public void run() {
if (serverFactoryBean.getServer() != null) {
serverFactoryBean.getServer().destroy();
}
if (serverFactoryBean.getBus() != null) {
serverFactoryBean.getBus().shutdown(true);
}
ProtocolServer httpServer = serverMap.get(addr);
if (httpServer != null) {
httpServer.close();
serverMap.remove(addr);
}
}
};
}
@Override
@SuppressWarnings("unchecked")
protected <T> T doRefer(final Class<T> serviceType, URL url) throws RpcException {
ClientProxyFactoryBean proxyFactoryBean = new ClientProxyFactoryBean();
String servicePathPrefix = url.getParameter(SERVICE_PATH_PREFIX);
if (!StringUtils.isEmpty(servicePathPrefix) && PROTOCOL_SERVER_SERVLET.equals(url.getParameter(PROTOCOL_SERVER))) {
url = url.setPath(servicePathPrefix + "/" + url.getPath());
}
String key = new URL("http", url.getHost(), url.getPort(), url.getPath(), url.getParameters()).toIdentityString();
proxyFactoryBean.setAddress(key);
proxyFactoryBean.setServiceClass(serviceType);
proxyFactoryBean.setBus(bus);
T ref = (T) proxyFactoryBean.create();
Client proxy = ClientProxy.getClient(ref);
HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
policy.setConnectionTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
policy.setReceiveTimeout(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT));
conduit.setClient(policy);
return ref;
}
@Override
protected int getErrorCode(Throwable e) {
if (e instanceof Fault) {
e = e.getCause();
}
if (e instanceof SocketTimeoutException) {
return RpcException.TIMEOUT_EXCEPTION;
} else if (e instanceof IOException) {
return RpcException.NETWORK_EXCEPTION;
}
return super.getErrorCode(e);
}
private class WebServiceHandler implements HttpHandler {
private volatile ServletController servletController;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (servletController == null) {
HttpServlet httpServlet = DispatcherServlet.getInstance();
if (httpServlet == null) {
response.sendError(500, "No such DispatcherServlet instance.");
return;
}
synchronized (this) {
if (servletController == null) {
if (server == null) {
server = WebServiceProtocol.this.serverFactoryBean.getServer();
}
Destination d = WebServiceProtocol.this.transportFactory.getDestination(server.getEndpoint().getEndpointInfo(), bus);
destinationRegistry.addDestination((AbstractHTTPDestination) d);
this.servletController = new ServletController(destinationRegistry, httpServlet.getServletConfig(), httpServlet);
}
}
}
RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
servletController.invoke(request, response);
}
}
private class URLHashMethodNameSoapActionServiceConfiguration extends AbstractServiceConfiguration {
public String getAction(OperationInfo op, Method method) {
String uri = op.getName().getNamespaceURI();
String action = op.getName().getLocalPart();
if (StringUtils.isEmpty(action)) {
action = method.getName();
}
return uri + "#" + action;
}
}
}