/* * 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.tuscany.sca.binding.rest.provider; import java.net.URI; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.servlet.Servlet; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import javax.xml.namespace.QName; import org.apache.tuscany.sca.binding.rest.RESTBinding; import org.apache.tuscany.sca.binding.rest.wireformat.json.JSONWireFormat; import org.apache.tuscany.sca.binding.rest.wireformat.xml.XMLWireFormat; import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.apache.tuscany.sca.core.invocation.ExtensibleProxyFactory; import org.apache.tuscany.sca.core.invocation.ProxyFactory; import org.apache.tuscany.sca.host.http.ServletHost; import org.apache.tuscany.sca.interfacedef.InterfaceContract; import org.apache.tuscany.sca.interfacedef.Operation; import org.apache.tuscany.sca.interfacedef.java.JavaInterface; import org.apache.tuscany.sca.interfacedef.java.jaxrs.RootResourceClassGenerator; import org.apache.tuscany.sca.invocation.Interceptor; import org.apache.tuscany.sca.invocation.InvocationChain; import org.apache.tuscany.sca.invocation.Invoker; import org.apache.tuscany.sca.invocation.MessageFactory; import org.apache.tuscany.sca.invocation.Phase; import org.apache.tuscany.sca.provider.EndpointProvider; import org.apache.tuscany.sca.provider.OperationSelectorProvider; import org.apache.tuscany.sca.provider.OperationSelectorProviderFactory; import org.apache.tuscany.sca.provider.ProviderFactoryExtensionPoint; import org.apache.tuscany.sca.provider.WireFormatProvider; import org.apache.tuscany.sca.provider.WireFormatProviderFactory; import org.apache.tuscany.sca.runtime.RuntimeComponent; import org.apache.tuscany.sca.runtime.RuntimeComponentService; import org.apache.tuscany.sca.runtime.RuntimeEndpoint; import org.apache.wink.server.utils.RegistrationUtils; import org.oasisopen.sca.ServiceRuntimeException; /** * Implementation of an HTTP binding provider. * * @version $Rev$ $Date$ */ public class RESTServiceBindingProvider implements EndpointProvider { private static Map<QName, String> wireFormatToMediaTypeMapping = new HashMap<QName, String>(); static { wireFormatToMediaTypeMapping.put(JSONWireFormat.REST_WIREFORMAT_JSON_QNAME, MediaType.APPLICATION_JSON); wireFormatToMediaTypeMapping.put(XMLWireFormat.REST_WIREFORMAT_XML_QNAME, MediaType.APPLICATION_XML); } private ExtensionPointRegistry extensionPoints; private RuntimeEndpoint endpoint; private RuntimeComponent component; private RuntimeComponentService service; private InterfaceContract serviceContract; private RESTBinding binding; private MessageFactory messageFactory; private OperationSelectorProvider osProvider; private WireFormatProvider wfProvider; private WireFormatProvider wfResponseProvider; private ServletHost servletHost; private String servletMapping; private RESTBindingListenerServlet bindingListenerServlet; private SimpleApplication application; public RESTServiceBindingProvider(RuntimeEndpoint endpoint, ExtensionPointRegistry extensionPoints, MessageFactory messageFactory, ServletHost servletHost) { this.endpoint = endpoint; this.component = (RuntimeComponent)endpoint.getComponent(); this.service = (RuntimeComponentService)endpoint.getService(); this.binding = (RESTBinding)endpoint.getBinding(); this.extensionPoints = extensionPoints; this.messageFactory = messageFactory; this.servletHost = servletHost; // retrieve operation selector and wire format service providers ProviderFactoryExtensionPoint providerFactories = extensionPoints.getExtensionPoint(ProviderFactoryExtensionPoint.class); if (binding.getOperationSelector() != null) { // Configure the interceptors for operation selection OperationSelectorProviderFactory osProviderFactory = (OperationSelectorProviderFactory)providerFactories.getProviderFactory(binding.getOperationSelector() .getClass()); if (osProviderFactory != null) { this.osProvider = osProviderFactory.createServiceOperationSelectorProvider(endpoint); } } if (binding.getRequestWireFormat() != null) { // Configure the interceptors for wire format WireFormatProviderFactory wfProviderFactory = (WireFormatProviderFactory)providerFactories.getProviderFactory(binding.getRequestWireFormat() .getClass()); if (wfProviderFactory != null) { this.wfProvider = wfProviderFactory.createServiceWireFormatProvider(endpoint); } } if (binding.getResponseWireFormat() != null) { // Configure the interceptors for wire format WireFormatProviderFactory wfProviderFactory = (WireFormatProviderFactory)providerFactories.getProviderFactory(binding.getResponseWireFormat() .getClass()); if (wfProviderFactory != null) { this.wfResponseProvider = wfProviderFactory.createServiceWireFormatProvider(endpoint); } } //clone the service contract to avoid databinding issues try { this.serviceContract = (InterfaceContract)service.getInterfaceContract().clone(); // configure data binding if (wfProvider != null) { wfProvider.configureWireFormatInterfaceContract(serviceContract); } if (wfResponseProvider != null) { wfResponseProvider.configureWireFormatInterfaceContract(serviceContract); } } catch (CloneNotSupportedException e) { this.serviceContract = service.getInterfaceContract(); } } public void start() { InvocationChain bindingChain = endpoint.getBindingInvocationChain(); application = registerWithJAXRS(); if (application != null) { return; } // Get the invokers for the supported operations Servlet servlet = null; Invoker bindingInvoker = bindingChain.getHeadInvoker(); bindingListenerServlet = new RESTBindingListenerServlet(binding, bindingInvoker, messageFactory); for (InvocationChain invocationChain : endpoint.getInvocationChains()) { Operation operation = invocationChain.getTargetOperation(); Invoker serviceInvoker = invocationChain.getHeadInvoker(); String operationName = operation.getName(); if (binding.getOperationSelector() != null || binding.getRequestWireFormat() != null) { bindingListenerServlet.setInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("get")) { bindingListenerServlet.setGetInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("conditionalGet")) { bindingListenerServlet.setConditionalGetInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("delete")) { bindingListenerServlet.setDeleteInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("conditionalDelete")) { bindingListenerServlet.setConditionalDeleteInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("put")) { bindingListenerServlet.setPutInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("conditionalPut")) { bindingListenerServlet.setConditionalPutInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("post")) { bindingListenerServlet.setPostInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("conditionalPost")) { bindingListenerServlet.setConditionalPostInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("service")) { servlet = new RESTServiceListenerServlet(binding, serviceInvoker, messageFactory); break; } } if (servlet == null) { throw new IllegalStateException("No get or service method found on the service"); } servletMapping = registerServlet(servlet); } public String registerServlet(Servlet servlet) { // Create our HTTP service listener Servlet and register it with the // Servlet host String servletMapping = binding.getURI(); if (!servletMapping.endsWith("/")) { servletMapping += "/"; } if (!servletMapping.endsWith("*")) { servletMapping += "*"; } String mappedURI = servletHost.addServletMapping(servletMapping, servlet); String deployedURI = mappedURI; if (deployedURI.endsWith("*")) { deployedURI = deployedURI.substring(0, deployedURI.length() - 1); } binding.setURI(deployedURI); return mappedURI; } public void stop() { if (application != null) { application.destroy(); } // Unregister the Servlet from the Servlet host servletHost.removeServletMapping(servletMapping); } public InterfaceContract getBindingInterfaceContract() { return serviceContract; } public boolean supportsOneWayInvocation() { return false; } /** * Register a Tuscany REST Servlet to handle JAX-RS Resources on a binding endpoint * @return */ private SimpleApplication registerWithJAXRS() { try { SimpleApplication application = null; JavaInterface javaInterface = (JavaInterface)endpoint.getComponentServiceInterfaceContract().getInterface(); Class<?> interfaze = javaInterface.getJavaClass(); // The @Path annotation can be from the binding uri boolean isJAXRS = JAXRSHelper.isJAXRSResource(interfaze); if (isJAXRS) { application = new SimpleApplication(interfaze); TuscanyRESTServlet restServlet = new TuscanyRESTServlet(extensionPoints, binding, application.resourceClass); servletMapping = registerServlet(restServlet); RegistrationUtils.registerApplication(application, restServlet.getServletContext()); return application; } else { return null; } } catch (Exception e) { throw new ServiceRuntimeException(e); } } private class SimpleApplication extends Application { private Class<?> resourceClass; public SimpleApplication(Class<?> resourceClass) { super(); if (resourceClass.isInterface()) { this.resourceClass = generateResourceClass(resourceClass); } else { this.resourceClass = resourceClass; } } @Override public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<Class<?>>(); classes.add(resourceClass); return classes; } private Class<?> generateResourceClass(Class<?> interfaze) { try { QName requestWireFormat = null; if (binding.getRequestWireFormat() != null) { requestWireFormat = binding.getRequestWireFormat().getSchemaName(); } QName responeWireFormat = null; if (binding.getResponseWireFormat() != null) { responeWireFormat = binding.getRequestWireFormat().getSchemaName(); } String requestMediaType = wireFormatToMediaTypeMapping.get(requestWireFormat); String responseMediaType = wireFormatToMediaTypeMapping.get(responeWireFormat); String uri = endpoint.getBinding().getURI(); String path = URI.create(uri).getPath(); // FIXME: [rfeng] We need to have a better way to deal with URI template for bindings if (path.startsWith(servletHost.getContextPath())) { path = path.substring(servletHost.getContextPath().length()); } Class<?> cls = RootResourceClassGenerator.generateRootResourceClass(interfaze, path, requestMediaType, responseMediaType); ProxyFactory proxyFactory = ExtensibleProxyFactory.getInstance(extensionPoints); Object proxy = proxyFactory.createProxy(interfaze, endpoint); RootResourceClassGenerator.injectProxy(cls, proxy); return cls; } catch (Exception e) { throw new ServiceRuntimeException(e); } } public void destroy() { resourceClass = null; } } /** * Add specific rest interceptor to invocation chain */ public void configure() { InvocationChain bindingChain = endpoint.getBindingInvocationChain(); if (wfProvider != null) { Interceptor interceptor = wfProvider.createInterceptor(); if (interceptor != null) { bindingChain.addInterceptor(Phase.SERVICE_BINDING_WIREFORMAT, interceptor); } } if (wfResponseProvider != null) { Interceptor interceptor = wfResponseProvider.createInterceptor(); if (interceptor != null) { bindingChain.addInterceptor(Phase.SERVICE_BINDING_WIREFORMAT, interceptor); } } if (osProvider != null) { Interceptor interceptor = osProvider.createInterceptor(); if (interceptor != null) { bindingChain.addInterceptor(Phase.SERVICE_BINDING_OPERATION_SELECTOR, interceptor); } } } }