From 88bf2a256b02e1858993bf097f4dc743d389e3f0 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Sun, 29 Aug 2010 02:55:29 +0000 Subject: Sandbox to experiment and extend the runtime. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@990479 13f79535-47bb-0310-9956-ffa450edef68 --- .../JAXRSOperationSelectorInterceptor.java | 256 +++++++++ .../JAXRSOperationSelectorProviderFactory.java | 52 ++ .../JAXRSOperationSelectorReferenceProvider.java | 50 ++ .../JAXRSOperationSelectorServiceProvider.java | 57 ++ .../provider/RPCOperationSelectorInterceptor.java | 203 +++++++ .../RPCOperationSelectorProviderFactory.java | 52 ++ .../RPCOperationSelectorReferenceProvider.java | 50 ++ .../RPCOperationSelectorServiceProvider.java | 57 ++ .../rest/provider/DataBindingJAXRSProvider.java | 203 +++++++ .../rest/provider/DataBindingJAXRSReader.java | 88 ++++ .../rest/provider/DataBindingJAXRSWriter.java | 89 ++++ .../sca/binding/rest/provider/JAXRSHelper.java | 80 +++ .../binding/rest/provider/RESTBindingInvoker.java | 298 +++++++++++ .../rest/provider/RESTBindingListenerServlet.java | 582 +++++++++++++++++++++ .../rest/provider/RESTBindingProviderFactory.java | 63 +++ .../provider/RESTReferenceBindingProvider.java | 64 +++ .../rest/provider/RESTServiceBindingProvider.java | 371 +++++++++++++ .../rest/provider/RESTServiceListenerServlet.java | 118 +++++ .../binding/rest/provider/TuscanyRESTServlet.java | 127 +++++ .../json/provider/JSONWireFormatInterceptor.java | 159 ++++++ .../provider/JSONWireFormatProviderFactory.java | 52 ++ .../provider/JSONWireFormatReferenceProvider.java | 54 ++ .../provider/JSONWireFormatServiceProvider.java | 136 +++++ .../xml/provider/XMLWireFormatInterceptor.java | 143 +++++ .../xml/provider/XMLWireFormatProviderFactory.java | 52 ++ .../provider/XMLWireFormatReferenceProvider.java | 54 ++ .../xml/provider/XMLWireFormatServiceProvider.java | 137 +++++ 27 files changed, 3647 insertions(+) create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorProviderFactory.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorReferenceProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorServiceProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorInterceptor.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorProviderFactory.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorReferenceProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorServiceProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/JAXRSHelper.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceListenerServlet.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatProviderFactory.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatReferenceProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatInterceptor.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatProviderFactory.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatReferenceProvider.java create mode 100644 sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java (limited to 'sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding') diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java new file mode 100644 index 0000000000..928b80a044 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java @@ -0,0 +1,256 @@ +/* + * 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.operationselector.jaxrs.provider; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.net.URLDecoder; +import java.util.List; + +import javax.activation.DataSource; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; + +import org.apache.tuscany.sca.common.http.HTTPContext; +import org.apache.tuscany.sca.common.http.HTTPUtil; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * JAXRS operation selector Interceptor. + * + * @version $Rev$ $Date$ +*/ +public class JAXRSOperationSelectorInterceptor implements Interceptor { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpoint endpoint; + + private RuntimeComponentService service; + private InterfaceContract interfaceContract; + private List serviceOperations; + + private Invoker next; + + public JAXRSOperationSelectorInterceptor(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + this.extensionPoints = extensionPoints; + this.endpoint = endpoint; + + this.service = (RuntimeComponentService)endpoint.getService(); + this.interfaceContract = service.getInterfaceContract(); + this.serviceOperations = service.getInterfaceContract().getInterface().getOperations(); + } + + public Invoker getNext() { + return next; + } + + public void setNext(Invoker next) { + this.next = next; + } + + public Message invoke(Message msg) { + try { + HTTPContext bindingContext = (HTTPContext)msg.getBindingContext(); + + // By-pass the operation selector + if (bindingContext == null) { + return getNext().invoke(msg); + } + + String path = URLDecoder.decode(HTTPUtil.getRequestPath(bindingContext.getHttpRequest()), "UTF-8"); + + if (path.startsWith("/")) { + path = path.substring(1); + } + + List operations = + filterOperationsByHttpMethod(interfaceContract, bindingContext.getHttpRequest().getMethod()); + + Operation operation = findOperation(path, operations); + + final JavaOperation javaOperation = (JavaOperation)operation; + final Method method = javaOperation.getJavaMethod(); + + if (path != null && path.length() > 0) { + if (method.getAnnotation(Path.class) != null) { + msg.setBody(new Object[] {path}); + } + } + + // FIXME: [rfeng] We should follow JAX-RS rules to identify the entity parameter + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length == 1) { + Class type = paramTypes[0]; + InputStream is = (InputStream)((Object[])msg.getBody())[0]; + Object target = convert(is, bindingContext.getHttpRequest().getContentType(), type); + msg.setBody(new Object[] {target}); + } else if (paramTypes.length == 0) { + msg.setBody(null); + } + + msg.setOperation(operation); + + return getNext().invoke(msg); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private Object convert(InputStream content, String contentType, Class type) { + if (type == DataSource.class) { + return type.cast(new InputStreamDataSource(content, contentType)); + } else if (type == InputStream.class) { + return type.cast(content); + } else if (type == String.class) { + try { + StringWriter sw = new StringWriter(); + InputStreamReader reader = new InputStreamReader(content, "UTF-8"); + char[] buf = new char[8192]; + while (true) { + int size = reader.read(buf); + if (size < 0) { + break; + } + sw.write(buf, 0, size); + } + return type.cast(sw.toString()); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } else if (type == byte[].class) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + while (true) { + int size = content.read(buf); + if (size < 0) { + break; + } + bos.write(buf, 0, size); + } + return type.cast(bos.toByteArray()); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } else { + return content; + } + } + + public static final class InputStreamDataSource implements DataSource { + + public static final String DEFAULT_TYPE = "application/octet-stream"; + + private final InputStream in; + private final String ctype; + + public InputStreamDataSource(InputStream in) { + this(in, null); + } + + public InputStreamDataSource(InputStream in, String ctype) { + this.in = in; + this.ctype = (ctype != null) ? ctype : DEFAULT_TYPE; + } + + public String getContentType() { + return ctype; + } + + public String getName() { + return null; + } + + public InputStream getInputStream() throws IOException { + return in; + } + + public OutputStream getOutputStream() throws IOException { + return null; + } + + } + + /** + * Find the operation from the component service contract + * @param componentService + * @param http_method + * @return + */ + private static List filterOperationsByHttpMethod(InterfaceContract interfaceContract, String http_method) { + List operations = null; + + if (http_method.equalsIgnoreCase("get")) { + operations = (List)interfaceContract.getInterface().getAttributes().get(GET.class); + } else if (http_method.equalsIgnoreCase("put")) { + operations = (List)interfaceContract.getInterface().getAttributes().get(PUT.class); + } else if (http_method.equalsIgnoreCase("post")) { + operations = (List)interfaceContract.getInterface().getAttributes().get(POST.class); + } else if (http_method.equalsIgnoreCase("delete")) { + operations = (List)interfaceContract.getInterface().getAttributes().get(DELETE.class); + } + + return operations; + } + + /** + * Find the operation from the component service contract + * @param componentService + * @param http_method + * @return + */ + private Operation findOperation(String path, List operations) { + Operation operation = null; + + for (Operation op : operations) { + final JavaOperation javaOperation = (JavaOperation)op; + final Method method = javaOperation.getJavaMethod(); + + if (path != null && path.length() > 0) { + if (method.getAnnotation(Path.class) != null) { + operation = op; + break; + } + } else { + if (method.getAnnotation(Path.class) == null) { + operation = op; + break; + } + } + } + + return operation; + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorProviderFactory.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorProviderFactory.java new file mode 100644 index 0000000000..d057f1a852 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorProviderFactory.java @@ -0,0 +1,52 @@ +/* + * 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.operationselector.jaxrs.provider; + +import org.apache.tuscany.sca.binding.rest.operationselector.jaxrs.JAXRSOperationSelector; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.provider.OperationSelectorProvider; +import org.apache.tuscany.sca.provider.OperationSelectorProviderFactory; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * JAXRS operation selector Provider Factory. + * + * @version $Rev$ $Date$ +*/ +public class JAXRSOperationSelectorProviderFactory implements OperationSelectorProviderFactory{ + private ExtensionPointRegistry extensionPoints; + + public JAXRSOperationSelectorProviderFactory(ExtensionPointRegistry extensionPoints) { + this.extensionPoints = extensionPoints; + } + public OperationSelectorProvider createReferenceOperationSelectorProvider(RuntimeEndpointReference endpointReference) { + return new JAXRSOperationSelectorReferenceProvider(extensionPoints, endpointReference); + } + + public OperationSelectorProvider createServiceOperationSelectorProvider(RuntimeEndpoint endpoint) { + return new JAXRSOperationSelectorServiceProvider(extensionPoints, endpoint); + } + + public Class getModelType() { + return JAXRSOperationSelector.class; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorReferenceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorReferenceProvider.java new file mode 100644 index 0000000000..c560a13ae2 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorReferenceProvider.java @@ -0,0 +1,50 @@ +/* + * 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.operationselector.jaxrs.provider; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.OperationSelectorProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * JAXRS operation selector Reference Provider. + * + * @version $Rev$ $Date$ +*/ +public class JAXRSOperationSelectorReferenceProvider implements OperationSelectorProvider { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpointReference endpointReference; + + public JAXRSOperationSelectorReferenceProvider(ExtensionPointRegistry extensionPoints, RuntimeEndpointReference endpointReference ) { + this.extensionPoints = extensionPoints; + this.endpointReference = endpointReference; + } + + public Interceptor createInterceptor() { + return null; + } + + public String getPhase() { + return Phase.SERVICE_BINDING_OPERATION_SELECTOR; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorServiceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorServiceProvider.java new file mode 100644 index 0000000000..105c84ebda --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorServiceProvider.java @@ -0,0 +1,57 @@ +/* + * 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.operationselector.jaxrs.provider; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.rest.operationselector.jaxrs.JAXRSOperationSelector; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.OperationSelectorProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * JAXRS operation selector Service Provider. + * + * @version $Rev$ $Date$ +*/ +public class JAXRSOperationSelectorServiceProvider implements OperationSelectorProvider { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpoint endpoint; + + private Binding binding; + + public JAXRSOperationSelectorServiceProvider(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + this.extensionPoints = extensionPoints; + this.endpoint = endpoint; + this.binding = endpoint.getBinding(); + } + + public Interceptor createInterceptor() { + if(binding.getOperationSelector() != null && binding.getOperationSelector() instanceof JAXRSOperationSelector) { + return new JAXRSOperationSelectorInterceptor(extensionPoints, endpoint); + } + return null; + } + + public String getPhase() { + return Phase.SERVICE_BINDING_OPERATION_SELECTOR; + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorInterceptor.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorInterceptor.java new file mode 100644 index 0000000000..0df6e5e0fb --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorInterceptor.java @@ -0,0 +1,203 @@ +/* + * 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.operationselector.rpc.provider; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.QueryParam; + +import org.apache.tuscany.sca.common.http.HTTPContext; +import org.apache.tuscany.sca.common.http.HTTPUtil; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.apache.tuscany.sca.databinding.SimpleTypeMapper; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.interfacedef.util.TypeInfo; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * RPC operation selector Interceptor. + * + * @version $Rev$ $Date$ +*/ +public class RPCOperationSelectorInterceptor implements Interceptor { + private ExtensionPointRegistry extensionPoints; + private SimpleTypeMapper simpleTypeMapper; + + private RuntimeEndpoint endpoint; + + private RuntimeComponentService service; + private InterfaceContract interfaceContract; + private List serviceOperations; + + private Invoker next; + + public RPCOperationSelectorInterceptor(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + this.extensionPoints = extensionPoints; + + UtilityExtensionPoint utilityExtensionPoint = extensionPoints.getExtensionPoint(UtilityExtensionPoint.class); + this.simpleTypeMapper = utilityExtensionPoint.getUtility(SimpleTypeMapper.class); + + this.endpoint = endpoint; + + this.service = (RuntimeComponentService)endpoint.getService(); + this.interfaceContract = service.getInterfaceContract(); + this.serviceOperations = service.getInterfaceContract().getInterface().getOperations(); + } + + public Invoker getNext() { + return next; + } + + public void setNext(Invoker next) { + this.next = next; + } + + public Message invoke(Message msg) { + try { + HTTPContext bindingContext = (HTTPContext)msg.getBindingContext(); + + if(! "get".equalsIgnoreCase(bindingContext.getHttpRequest().getMethod())) { + throw new RuntimeException("RPC Invocation only allowed over HTTP GET operations"); + } + + String path = URLDecoder.decode(HTTPUtil.getRequestPath(bindingContext.getHttpRequest()), "UTF-8"); + + if (path.startsWith("/")) { + path = path.substring(1); + } + + + String operationName = bindingContext.getHttpRequest().getParameter("method"); + Operation operation = findOperation( operationName ); + + if (operation == null) { + throw new RuntimeException("Invalid Operation '" + operationName + "'" ); + } + + final JavaOperation javaOperation = (JavaOperation)operation; + final Method method = javaOperation.getJavaMethod(); + + List messageParameters = new ArrayList(); + for(int i=0; i clazz = method.getParameterTypes()[i]; + TypeInfo typeInfo = simpleTypeMapper.getXMLType(clazz); + Object v = simpleTypeMapper.toJavaObject(typeInfo.getQName(), values[0], null); + messageParameters.add(v); + } else { + //process value, making necessary map from string to expected value + Class clazz = (method.getParameterTypes()[i]).getComponentType(); + TypeInfo typeInfo = simpleTypeMapper.getXMLType(clazz); + + + Object objectArray = Array.newInstance(clazz, values.length); + for (int count = 0; count < values.length; ++count) { + Object v = simpleTypeMapper.toJavaObject(typeInfo.getQName(), values[count], null); + Array.set(objectArray, count, v); + } + + messageParameters.add(objectArray); + } + } + } + } + + Object[] body = new Object[messageParameters.size()]; + messageParameters.toArray(body); + + msg.setBody(body); + msg.setOperation(operation); + + Message responseMessage = getNext().invoke(msg); + + //set Cache-Control to no-cache to avoid intermediary + //proxy/reverse-proxy caches and always hit the server + //that would identify if the value was current or not + bindingContext.getHttpResponse().setHeader("Cache-Control", "no-cache"); + bindingContext.getHttpResponse().setHeader("Expires", new Date(0).toGMTString()); + + + String eTag = HTTPUtil.calculateHashETag(responseMessage.getBody().toString().getBytes("UTF-8")); + + // Test request for predicates. + String predicate = bindingContext.getHttpRequest().getHeader( "If-Match" ); + if (( predicate != null ) && ( !predicate.equals(eTag) )) { + // No match, should short circuit + bindingContext.getHttpResponse().sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + } + predicate = bindingContext.getHttpRequest().getHeader( "If-None-Match" ); + if (( predicate != null ) && ( predicate.equals(eTag) )) { + // Match, should short circuit + bindingContext.getHttpResponse().sendError(HttpServletResponse.SC_NOT_MODIFIED); + } + + bindingContext.getHttpResponse().addHeader("ETag", eTag); + + return responseMessage; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Find the operation from the component service contract + * @param componentService + * @param method + * @return + */ + private Operation findOperation(String method) { + if (method.contains(".")) { + method = method.substring(method.lastIndexOf(".") + 1); + } + + List operations = endpoint.getComponentServiceInterfaceContract().getInterface().getOperations(); + + Operation result = null; + for (Operation o : operations) { + if (o.getName().equalsIgnoreCase(method)) { + result = o; + break; + } + } + + return result; + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorProviderFactory.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorProviderFactory.java new file mode 100644 index 0000000000..011e89e7cc --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorProviderFactory.java @@ -0,0 +1,52 @@ +/* + * 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.operationselector.rpc.provider; + +import org.apache.tuscany.sca.binding.rest.operationselector.rpc.RPCOperationSelector; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.provider.OperationSelectorProvider; +import org.apache.tuscany.sca.provider.OperationSelectorProviderFactory; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * RPC operation selector Provider Factory. + * + * @version $Rev$ $Date$ +*/ +public class RPCOperationSelectorProviderFactory implements OperationSelectorProviderFactory{ + private ExtensionPointRegistry extensionPoints; + + public RPCOperationSelectorProviderFactory(ExtensionPointRegistry extensionPoints) { + this.extensionPoints = extensionPoints; + } + public OperationSelectorProvider createReferenceOperationSelectorProvider(RuntimeEndpointReference endpointReference) { + return new RPCOperationSelectorReferenceProvider(extensionPoints, endpointReference); + } + + public OperationSelectorProvider createServiceOperationSelectorProvider(RuntimeEndpoint endpoint) { + return new RPCOperationSelectorServiceProvider(extensionPoints, endpoint); + } + + public Class getModelType() { + return RPCOperationSelector.class; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorReferenceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorReferenceProvider.java new file mode 100644 index 0000000000..c87b2fc21d --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorReferenceProvider.java @@ -0,0 +1,50 @@ +/* + * 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.operationselector.rpc.provider; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.OperationSelectorProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * RPC operation selector Reference Provider. + * + * @version $Rev$ $Date$ +*/ +public class RPCOperationSelectorReferenceProvider implements OperationSelectorProvider { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpointReference endpointReference; + + public RPCOperationSelectorReferenceProvider(ExtensionPointRegistry extensionPoints, RuntimeEndpointReference endpointReference ) { + this.extensionPoints = extensionPoints; + this.endpointReference = endpointReference; + } + + public Interceptor createInterceptor() { + return null; + } + + public String getPhase() { + return Phase.SERVICE_BINDING_OPERATION_SELECTOR; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorServiceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorServiceProvider.java new file mode 100644 index 0000000000..e4a003d4b5 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/rpc/provider/RPCOperationSelectorServiceProvider.java @@ -0,0 +1,57 @@ +/* + * 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.operationselector.rpc.provider; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.rest.operationselector.rpc.RPCOperationSelector; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.OperationSelectorProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * RPC operation selector Service Provider. + * + * @version $Rev$ $Date$ +*/ +public class RPCOperationSelectorServiceProvider implements OperationSelectorProvider { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpoint endpoint; + + private Binding binding; + + public RPCOperationSelectorServiceProvider(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + this.extensionPoints = extensionPoints; + this.endpoint = endpoint; + this.binding = endpoint.getBinding(); + } + + public Interceptor createInterceptor() { + if(binding.getOperationSelector() != null && binding.getOperationSelector() instanceof RPCOperationSelector) { + return new RPCOperationSelectorInterceptor(extensionPoints, endpoint); + } + return null; + } + + public String getPhase() { + return Phase.SERVICE_BINDING_OPERATION_SELECTOR; + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java new file mode 100644 index 0000000000..1ccdb868d7 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java @@ -0,0 +1,203 @@ +/* + * 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.activation.DataSource; +import javax.jws.WebParam; +import javax.jws.WebResult; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.Provider; +import javax.xml.namespace.QName; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.apache.tuscany.sca.databinding.DataBindingExtensionPoint; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +import org.apache.tuscany.sca.interfacedef.util.XMLType; + +/** + * A JAX-RS provider that leverages Tuscany's databinding framework to handle read/write + * for JAX-RS runtime + */ +@Provider +public abstract class DataBindingJAXRSProvider { + protected DataBindingExtensionPoint dataBindingExtensionPoint; + protected Mediator mediator; + + public DataBindingJAXRSProvider(ExtensionPointRegistry registry) { + this.dataBindingExtensionPoint = registry.getExtensionPoint(DataBindingExtensionPoint.class); + UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class); + this.mediator = utilities.getUtility(Mediator.class); + } + + protected A getAnnotation(Annotation[] annotations, Class type) { + for (Annotation a : annotations) { + if (a.annotationType() == type) { + return type.cast(a); + } + } + return null; + } + + protected void introspectAnnotations(Annotation[] annotations, DataType targetDataType) { + WebResult result = getAnnotation(annotations, WebResult.class); + if (result != null) { + QName name = new QName(result.targetNamespace(), result.name()); + targetDataType.setLogical(new XMLType(name, null)); + } + + WebParam param = getAnnotation(annotations, WebParam.class); + if (param != null) { + QName name = new QName(param.targetNamespace(), param.name()); + targetDataType.setLogical(new XMLType(name, null)); + } + } + + protected DataType createDataType(Class type, Type genericType) { + DataType dataType = new DataTypeImpl(null, type, type, genericType); + dataBindingExtensionPoint.introspectType(dataType, null); + return dataType; + } + + protected boolean supports(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + // Some media types have parameters + mediaType = new MediaType(mediaType.getType(), mediaType.getSubtype()); + return MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType) || MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType) + || MediaType.TEXT_XML_TYPE.isCompatible(mediaType); + } + + protected Object convert(InputStream content, String contentType, Class type) throws IOException { + if (type == DataSource.class) { + return type.cast(new InputStreamDataSource(content, contentType)); + } else if (type == InputStream.class) { + return type.cast(content); + } else if (type == Reader.class) { + return type.cast(new InputStreamReader(content, "UTF-8")); + } else if (type == String.class) { + try { + StringWriter sw = new StringWriter(); + InputStreamReader reader = new InputStreamReader(content, "UTF-8"); + char[] buf = new char[8192]; + while (true) { + int size = reader.read(buf); + if (size < 0) { + break; + } + sw.write(buf, 0, size); + } + return type.cast(sw.toString()); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } else if (type == byte[].class) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + while (true) { + int size = content.read(buf); + if (size < 0) { + break; + } + bos.write(buf, 0, size); + } + return type.cast(bos.toByteArray()); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } else { + return content; + } + } + + protected void write(OutputStream out, Object content, Class type) throws IOException { + if (content == null) { + return; + } + InputStream in = null; + if (DataSource.class.isAssignableFrom(type)) { + in = ((DataSource)content).getInputStream(); + } else if (InputStream.class.isAssignableFrom(type)) { + in = (InputStream)content; + } else if (type == String.class) { + in = new ByteArrayInputStream(((String)content).getBytes("UTF-8")); + } else if (type == byte[].class) { + in = new ByteArrayInputStream((byte[])content); + } + if (in == null) { + throw new IllegalArgumentException("Type is not supported: " + type); + } + byte[] buf = new byte[8192]; + while (true) { + int len = in.read(buf); + if (len < 0) { + in.close(); + break; + } + out.write(buf, 0, len); + } + } + + public static final class InputStreamDataSource implements DataSource { + + public static final String DEFAULT_TYPE = "application/octet-stream"; + + private final InputStream in; + private final String ctype; + + public InputStreamDataSource(InputStream in) { + this(in, null); + } + + public InputStreamDataSource(InputStream in, String ctype) { + this.in = in; + this.ctype = (ctype != null) ? ctype : DEFAULT_TYPE; + } + + public String getContentType() { + return ctype; + } + + public String getName() { + return null; + } + + public InputStream getInputStream() throws IOException { + return in; + } + + public OutputStream getOutputStream() throws IOException { + return null; + } + + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java new file mode 100644 index 0000000000..d38881eb28 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java @@ -0,0 +1,88 @@ +/* + * 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.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; + +/** + * The generic JAX-RS message body reader based on Tuscany's databindingframework + */ +@Provider +public class DataBindingJAXRSReader extends DataBindingJAXRSProvider implements MessageBodyReader { + + public DataBindingJAXRSReader(ExtensionPointRegistry registry) { + super(registry); + } + + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { +// DataType dataType = createDataType(type, genericType); + return supports(type, genericType, annotations, mediaType); + } + + public T readFrom(Class type, + Type genericType, + Annotation[] annotations, + MediaType mediaType, + MultivaluedMap httpHeaders, + InputStream entityStream) throws IOException, WebApplicationException { + + Object source = entityStream; + DataType targetDataType = createDataType(type, genericType); + + String dataBinding = null; + + mediaType = new MediaType(mediaType.getType(), mediaType.getSubtype()); + // FIXME: [rfeng] This is a hack to handle application/json + if (MediaType.APPLICATION_JSON_TYPE.equals(mediaType)) { + dataBinding = mediaType.toString() + "#" + InputStream.class.getName(); + } else if ("application/x-protobuf".equals(mediaType.toString())) { + dataBinding = mediaType.toString() + "#" + InputStream.class.getName(); + } + else if (MediaType.APPLICATION_XML_TYPE.equals(mediaType) || MediaType.TEXT_XML_TYPE.equals(mediaType)) { + dataBinding = InputStream.class.getName(); + } else { + dataBinding = targetDataType.getDataBinding(); + source = convert(entityStream, mediaType.toString(), type); + return (T) source; + } + DataType sourceDataType = + new DataTypeImpl(dataBinding, InputStream.class, InputStream.class, InputStream.class); + + Object result = mediator.mediate(source, sourceDataType, targetDataType, Collections.emptyMap()); + return (T)result; + } + + + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java new file mode 100644 index 0000000000..be2a9555d7 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java @@ -0,0 +1,89 @@ +/* + * 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.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; + +/** + * The generic JAX-RS message body writer based on Tuscany's databindingframework + */ +@Provider +public class DataBindingJAXRSWriter extends DataBindingJAXRSProvider implements MessageBodyWriter { + + public DataBindingJAXRSWriter(ExtensionPointRegistry registry) { + super(registry); + } + + public long getSize(T t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return -1; + } + + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + // DataType dataType = createDataType(type, genericType); + return supports(type, genericType, annotations, mediaType); + } + + public void writeTo(T t, + Class type, + Type genericType, + Annotation[] annotations, + MediaType mediaType, + MultivaluedMap httpHeaders, + OutputStream entityStream) throws IOException, WebApplicationException { + DataType dataType = createDataType(type, genericType); + mediaType = new MediaType(mediaType.getType(), mediaType.getSubtype()); + String dataBinding = OutputStream.class.getName(); + // FIXME: [rfeng] This is a hack to handle application/json + if (MediaType.APPLICATION_JSON_TYPE.equals(mediaType)) { + dataBinding = mediaType.toString() + "#" + OutputStream.class.getName(); + } else if (MediaType.APPLICATION_XML_TYPE.equals(mediaType) || MediaType.TEXT_XML_TYPE.equals(mediaType)) { + dataBinding = OutputStream.class.getName(); + } else if ("application/x-protobuf".equals(mediaType.toString())) { + dataBinding = mediaType.toString() + "#" + OutputStream.class.getName(); + } + else { + dataBinding = dataType.getDataBinding(); + write(entityStream, t, type); + return; + } + DataType targetDataType = + new DataTypeImpl(dataBinding, OutputStream.class, OutputStream.class, OutputStream.class); + // dataBindingExtensionPoint.introspectType(targetDataType, null); + + introspectAnnotations(annotations, targetDataType); + + mediator.mediate(t, entityStream, dataType, targetDataType, Collections. emptyMap()); + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/JAXRSHelper.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/JAXRSHelper.java new file mode 100644 index 0000000000..2bf95f0677 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/JAXRSHelper.java @@ -0,0 +1,80 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import javax.ws.rs.HttpMethod; +import javax.ws.rs.Path; + +/** + * A utility class that deals with JAX-RS annotations + */ +public class JAXRSHelper { + private JAXRSHelper() { + + } + + /** + * A resource class is a Java class that uses JAX-RS annotations to implement a corresponding Web resource. + * Resource classes are POJOs that have at least one method annotated with @Path or a request method designator. + * @param cls + * @return + */ + public static boolean isJAXRSResource(Class cls) { + for (Method method : cls.getMethods()) { + if (method.isAnnotationPresent(Path.class)) { + return true; + } + if (isResourceMethod(method)) { + return true; + } + } + return false; + } + + /** + * Root resource class is a resource class annotated with @Path. Root resource classes provide the roots of the + * resource class tree and provide access to sub-resources + * @param cls + * @return + */ + public static boolean isJAXRSRootResource(Class cls) { + return cls.isAnnotationPresent(Path.class) && isJAXRSResource(cls); + } + + public static boolean isResourceMethod(Method method) { + for (Annotation a : method.getAnnotations()) { + Class annotationType = a.annotationType(); + if (annotationType == HttpMethod.class) { + return true; + } + // Http method related annotations such as @GET, @POST will have itself annotated with + // @HttpMethod + HttpMethod m = a.annotationType().getAnnotation(HttpMethod.class); + if (m != null) { + return true; + } + } + return false; + + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java new file mode 100644 index 0000000000..1673f3aefa --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java @@ -0,0 +1,298 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.CookieParam; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.MatrixParam; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriBuilder; + +import org.apache.tuscany.sca.assembly.WireFormat; +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.common.http.HTTPCacheContext; +import org.apache.tuscany.sca.common.http.HTTPHeader; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.wink.client.ClientConfig; +import org.apache.wink.client.Resource; +import org.apache.wink.client.RestClient; +import org.apache.wink.client.handlers.BasicAuthSecurityHandler; + +/** + * + */ +public class RESTBindingInvoker implements Invoker { + private ExtensionPointRegistry registry; + private RESTBinding binding; + private Operation operation; + private RestClient restClient; + private String httpMethod; + private Class responseType; + + public RESTBindingInvoker(ExtensionPointRegistry registry, RESTBinding binding, Operation operation) { + super(); + this.registry = registry; + this.binding = binding; + this.operation = operation; + this.restClient = createRestClient(); + } + + private static Map, String> mapping = new HashMap, String>(); + static { + mapping.put(GET.class, HttpMethod.GET); + mapping.put(POST.class, HttpMethod.POST); + mapping.put(PUT.class, HttpMethod.PUT); + mapping.put(DELETE.class, HttpMethod.DELETE); + mapping.put(HEAD.class, HttpMethod.HEAD); + mapping.put(OPTIONS.class, HttpMethod.OPTIONS); + } + + private static T getAnnotation(Annotation[] annotations, Class type) { + for (Annotation a : annotations) { + if (a.annotationType() == type) { + return type.cast(a); + } + } + return null; + } + + private RestClient createRestClient() { + ClientConfig config = new ClientConfig(); + + // configureBasicAuth(config, userName, password); + + config.applications(new Application() { + + @Override + public Set> getClasses() { + return Collections.emptySet(); + } + + @Override + public Set getSingletons() { + Set providers = new HashSet(); + providers.add(new DataBindingJAXRSReader(registry)); + providers.add(new DataBindingJAXRSWriter(registry)); + return providers; + } + + }); + RestClient client = new RestClient(config); + + // Default to GET for RPC + httpMethod = HttpMethod.GET; + + for (Map.Entry, String> e : mapping.entrySet()) { + if (operation.getAttributes().get(e.getKey()) != null) { + httpMethod = e.getValue(); + break; + } + } + + if (operation.getOutputType() != null) { + responseType = operation.getOutputType().getPhysical(); + } else { + responseType = null; + } + return client; + } + + private void configureBasicAuth(ClientConfig config, String userName, String password) { + BasicAuthSecurityHandler basicAuthSecurityHandler = new BasicAuthSecurityHandler(); + basicAuthSecurityHandler.setUserName(userName); + basicAuthSecurityHandler.setPassword(password); + config.handlers(basicAuthSecurityHandler); + } + + public Message invoke(Message msg) { + + Object entity = null; + Object[] args = msg.getBody(); + + URI uri = URI.create(binding.getURI()); + UriBuilder uriBuilder = UriBuilder.fromUri(uri); + + Method method = ((JavaOperation)operation).getJavaMethod(); + + if (method.isAnnotationPresent(Path.class)) { + // Only for resource method + uriBuilder.path(method); + } + + if (!JAXRSHelper.isResourceMethod(method)) { + // This is RPC over GET + uriBuilder.replaceQueryParam("method", method.getName()); + } + + Map pathParams = new HashMap(); + Map matrixParams = new HashMap(); + Map queryParams = new HashMap(); + Map headerParams = new HashMap(); + Map formParams = new HashMap(); + Map cookieParams = new HashMap(); + + for (int i = 0; i < method.getParameterTypes().length; i++) { + boolean isEntity = true; + Annotation[] annotations = method.getParameterAnnotations()[i]; + PathParam pathParam = getAnnotation(annotations, PathParam.class); + if (pathParam != null) { + isEntity = false; + pathParams.put(pathParam.value(), args[i]); + } + MatrixParam matrixParam = getAnnotation(annotations, MatrixParam.class); + if (matrixParam != null) { + isEntity = false; + matrixParams.put(matrixParam.value(), args[i]); + } + QueryParam queryParam = getAnnotation(annotations, QueryParam.class); + if (queryParam != null) { + isEntity = false; + queryParams.put(queryParam.value(), args[i]); + } + HeaderParam headerParam = getAnnotation(annotations, HeaderParam.class); + if (headerParam != null) { + isEntity = false; + headerParams.put(headerParam.value(), args[i]); + } + FormParam formParam = getAnnotation(annotations, FormParam.class); + if (formParam != null) { + isEntity = false; + formParams.put(formParam.value(), args[i]); + } + CookieParam cookieParam = getAnnotation(annotations, CookieParam.class); + if (cookieParam != null) { + isEntity = false; + cookieParams.put(cookieParam.value(), args[i]); + } + if (isEntity) { + entity = args[i]; + } + } + + for (Map.Entry p : queryParams.entrySet()) { + uriBuilder.replaceQueryParam(p.getKey(), p.getValue()); + } + for (Map.Entry p : matrixParams.entrySet()) { + uriBuilder.replaceMatrixParam(p.getKey(), p.getValue()); + } + + uri = uriBuilder.buildFromMap(pathParams); + Resource resource = restClient.resource(uri); + + for (Map.Entry p : headerParams.entrySet()) { + resource.header(p.getKey(), String.valueOf(p.getValue())); + } + + for (Map.Entry p : cookieParams.entrySet()) { + Cookie cookie = new Cookie(p.getKey(), String.valueOf(p.getValue())); + resource.cookie(cookie); + } + + resource.contentType(getContentType()); + resource.accept(getAccepts()); + + //handles declarative headers configured on the composite + for (HTTPHeader header : binding.getHttpHeaders()) { + //treat special headers that need to be calculated + if (header.getName().equalsIgnoreCase("Expires")) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(new Date()); + + calendar.add(Calendar.HOUR, Integer.parseInt(header.getValue())); + + resource.header("Expires", HTTPCacheContext.RFC822DateFormat.format(calendar.getTime())); + } else { + //default behaviour to pass the header value to HTTP response + resource.header(header.getName(), header.getValue()); + } + } + + Object result = resource.invoke(httpMethod, responseType, entity); + msg.setBody(result); + return msg; + } + + private String getContentType() { + String contentType = MediaType.APPLICATION_OCTET_STREAM; + Consumes consumes = ((JavaOperation)operation).getJavaMethod().getAnnotation(Consumes.class); + if (consumes != null && consumes.value().length > 0) { + contentType = consumes.value()[0]; + } + WireFormat wf = binding.getRequestWireFormat(); + if (wf != null) { + if (XMLWireFormat.REST_WIREFORMAT_XML_QNAME.equals(wf.getSchemaName())) { + contentType = MediaType.APPLICATION_XML; + } else if (JSONWireFormat.REST_WIREFORMAT_JSON_QNAME.equals(wf.getSchemaName())) { + contentType = MediaType.APPLICATION_JSON; + } + } + return contentType; + } + + private String[] getAccepts() { + String accepts[] = {MediaType.APPLICATION_OCTET_STREAM}; + Produces produces = ((JavaOperation)operation).getJavaMethod().getAnnotation(Produces.class); + if (produces != null) { + accepts = produces.value(); + } + WireFormat wf = binding.getResponseWireFormat(); + if (wf != null) { + if (XMLWireFormat.REST_WIREFORMAT_XML_QNAME.equals(wf.getSchemaName())) { + accepts = new String[] {MediaType.APPLICATION_XML}; + } else if (JSONWireFormat.REST_WIREFORMAT_JSON_QNAME.equals(wf.getSchemaName())) { + accepts = new String[] {MediaType.APPLICATION_JSON}; + } + } + return accepts; + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java new file mode 100644 index 0000000000..4583b11879 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java @@ -0,0 +1,582 @@ +/* + * 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.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLDecoder; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.rest.RESTBinding; +import org.apache.tuscany.sca.common.http.HTTPCacheContext; +import org.apache.tuscany.sca.common.http.HTTPContentTypeMapper; +import org.apache.tuscany.sca.common.http.HTTPContext; +import org.apache.tuscany.sca.common.http.HTTPHeader; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.invocation.MessageFactory; + +/** + * Servlet responsible for dispatching HTTP requests to the + * target component implementation. + * + * @version $Rev$ $Date$ + */ +public class RESTBindingListenerServlet extends HttpServlet { + private static final long serialVersionUID = 2865466417329430610L; + + transient private MessageFactory messageFactory; + + transient private RESTBinding binding; + transient private Invoker bindingInvoker; + + private Invoker invoker; + private Invoker getInvoker; + private Invoker conditionalGetInvoker; + private Invoker putInvoker; + private Invoker conditionalPutInvoker; + private Invoker postInvoker; + private Invoker conditionalPostInvoker; + private Invoker deleteInvoker; + private Invoker conditionalDeleteInvoker; + + /** + * Constructs a new RESTServiceListenerServlet. + */ + public RESTBindingListenerServlet(Binding binding, Invoker bindingInvoker, MessageFactory messageFactory) { + this.binding = (RESTBinding) binding; + this.bindingInvoker = bindingInvoker; + this.messageFactory = messageFactory; + } + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if( binding.getOperationSelector() != null || binding.getRequestWireFormat() != null) { + HTTPContext bindingContext = new HTTPContext(); + bindingContext.setHttpRequest(request); + bindingContext.setHttpResponse(response); + + // Dispatch the service interaction to the service invoker + Message requestMessage = messageFactory.createMessage(); + requestMessage.setBindingContext(bindingContext); + + requestMessage.setBody(new Object[] {request.getInputStream()}); + + Message responseMessage = bindingInvoker.invoke(requestMessage); + + // return response to client + if (responseMessage.isFault()) { + // Turn a fault into an exception + Throwable e = (Throwable)responseMessage.getBody(); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString()); + } else { + + for(HTTPHeader header : binding.getHttpHeaders()) { + //treat special headers that need to be calculated + if(header.getName().equalsIgnoreCase("Expires")) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(new Date()); + + calendar.add(Calendar.HOUR, Integer.parseInt(header.getValue())); + + response.setHeader("Expires", HTTPCacheContext.RFC822DateFormat.format( calendar.getTime() )); + } else { + //default behaviour to pass the header value to HTTP response + response.setHeader(header.getName(), header.getValue()); + } + + } + + //handle void operations + write(response.getOutputStream(), responseMessage.getBody()); + response.getOutputStream().flush(); + response.getOutputStream().close(); + } + } else { + super.service(request, response); + } + + + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // Get the request path + String path = URLDecoder.decode(request.getRequestURI().substring(request.getServletPath().length()), "UTF-8"); + if (path.length() ==0) { + // Redirect to a URL ending with / to make relative hrefs work + // relative to the served resource. + response.sendRedirect(request.getRequestURL().append('/').toString()); + return; + } + + // Invoke the get operation on the service implementation + Message requestMessage = messageFactory.createMessage(); + + String id = path.substring(1); + + Message responseMessage = null; + HTTPCacheContext cacheContext = null; + try { + cacheContext = HTTPCacheContext.createCacheContextFromRequest(request); + } catch (ParseException e) { + + } + + if (path == null || path.length() == 0 || path.equals("/")) { + + } + + // Route message based on availability of cache info and cache methods + if (( cacheContext != null ) && (cacheContext.isEnabled()) && (conditionalGetInvoker != null )) { + if(id != null && id.length() > 0) { + requestMessage.setBody(new Object[] {id, cacheContext}); + } else { + requestMessage.setBody(new Object[] {cacheContext}); + } + + responseMessage = conditionalGetInvoker.invoke(requestMessage); + } else { + if(id != null && id.length() > 0) { + requestMessage.setBody(new Object[] {id}); + } else { + //requestMessage.setBody(new Object[] {id}); + } + + responseMessage = getInvoker.invoke(requestMessage); + } + + if (responseMessage.isFault()) { + Object body = responseMessage.getBody(); + + int index = -1; + if ( -1 < (index = body.getClass().getName().indexOf( "NotModifiedException")) ) { + if ( index > -1 ) { + response.sendError( HttpServletResponse.SC_NOT_MODIFIED, body.toString().substring( index )); + } else { + response.sendError( HttpServletResponse.SC_NOT_MODIFIED ); + } + return; + } else if ( -1 < (index = body.getClass().getName().indexOf( "PreconditionFailedException")) ) { + if ( index > -1 ) { + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED, body.toString().substring( index )); + } else { + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED ); + } + return; + } + + throw new ServletException((Throwable)responseMessage.getBody()); + } + + if(response.getContentType() == null || response.getContentType().length() == 0){ + // Calculate content-type based on extension + String contentType = HTTPContentTypeMapper.getContentType(id); + if(contentType != null && contentType.length() >0) { + response.setContentType(contentType); + } + } + + // Write the response from the service implementation to the response + // output stream + InputStream is = (InputStream)responseMessage.getBody(); + OutputStream os = response.getOutputStream(); + byte[] buffer = new byte[2048]; + for (;;) { + int n = is.read(buffer); + if (n <= 0) + break; + os.write(buffer, 0, n); + } + os.flush(); + os.close(); + } + + @Override + protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // Get the request path + String path = URLDecoder.decode(request.getRequestURI().substring(request.getServletPath().length()), "UTF-8"); + if (path.length() ==0) { + // Redirect to a URL ending with / to make relative hrefs work + // relative to the served resource. + response.sendRedirect(request.getRequestURL().append('/').toString()); + return; + } + + // Invoke the get operation on the service implementation + Message requestMessage = messageFactory.createMessage(); + String id = path.substring(1); + + Message responseMessage = null; + HTTPCacheContext cacheContext = null; + try { + cacheContext = HTTPCacheContext.createCacheContextFromRequest(request); + } catch (ParseException e) { + } + + // Route message based on availability of cache info and cache methods + if (( cacheContext != null ) && (cacheContext.isEnabled()) && (conditionalDeleteInvoker != null )) { + requestMessage.setBody(new Object[] {id, cacheContext}); + responseMessage = conditionalDeleteInvoker.invoke(requestMessage); + } else { + requestMessage.setBody(new Object[] {id}); + responseMessage = deleteInvoker.invoke(requestMessage); + } + if (responseMessage.isFault()) { + Object body = responseMessage.getBody(); + + int index = -1; + if ( -1 < (index = body.getClass().getName().indexOf( "NotModifiedException")) ) { + if ( index > -1 ) { + response.sendError( HttpServletResponse.SC_NOT_MODIFIED, body.toString().substring( index )); + } else { + response.sendError( HttpServletResponse.SC_NOT_MODIFIED ); + } + return; + } else if ( -1 < (index = body.getClass().getName().indexOf( "PreconditionFailedException")) ) { + if ( index > -1 ) { + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED, body.toString().substring( index )); + } else { + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED ); + } + return; + } + + throw new ServletException((Throwable)responseMessage.getBody()); + } + + // Write the response from the service implementation to the response + // output stream + InputStream is = (InputStream)responseMessage.getBody(); + OutputStream os = response.getOutputStream(); + byte[] buffer = new byte[2048]; + for (;;) { + int n = is.read(buffer); + if (n <= 0) + break; + os.write(buffer, 0, n); + } + os.flush(); + os.close(); + } + + @Override + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // Get the request path + String path = URLDecoder.decode(request.getRequestURI().substring(request.getServletPath().length()), "UTF-8"); + if (path.length() ==0) { + // Redirect to a URL ending with / to make relative hrefs work + // relative to the served resource. + response.sendRedirect(request.getRequestURL().append('/').toString()); + return; + } + + // Invoke the get operation on the service implementation + Message requestMessage = messageFactory.createMessage(); + String id = path.substring(1); + + Message responseMessage = null; + HTTPCacheContext cacheContext = null; + try { + cacheContext = HTTPCacheContext.createCacheContextFromRequest(request); + } catch (ParseException e) { + } + + // Route message based on availability of cache info and cache methods + if (( cacheContext != null ) && (cacheContext.isEnabled()) && (conditionalPutInvoker != null )) { + requestMessage.setBody(new Object[] {id, cacheContext}); + responseMessage = conditionalPutInvoker.invoke(requestMessage); + } else { + requestMessage.setBody(new Object[] {id}); + responseMessage = putInvoker.invoke(requestMessage); + } + if (responseMessage.isFault()) { + Object body = responseMessage.getBody(); + + int index = -1; + if ( -1 < (index = body.getClass().getName().indexOf( "NotModifiedException")) ) { + if ( index > -1 ) { + response.sendError( HttpServletResponse.SC_NOT_MODIFIED, body.toString().substring( index )); + } else { + response.sendError( HttpServletResponse.SC_NOT_MODIFIED ); + } + return; + } else if ( -1 < (index = body.getClass().getName().indexOf( "PreconditionFailedException")) ) { + if ( index > -1 ) { + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED, body.toString().substring( index )); + } else { + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED ); + } + return; + } + + throw new ServletException((Throwable)responseMessage.getBody()); + } + + // Write the response from the service implementation to the response + // output stream + InputStream is = (InputStream)responseMessage.getBody(); + OutputStream os = response.getOutputStream(); + byte[] buffer = new byte[2048]; + for (;;) { + int n = is.read(buffer); + if (n <= 0) + break; + os.write(buffer, 0, n); + } + os.flush(); + os.close(); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // Get the request path + String path = URLDecoder.decode(request.getRequestURI().substring(request.getServletPath().length()), "UTF-8"); + if (path.length() ==0) { + // Redirect to a URL ending with / to make relative hrefs work + // relative to the served resource. + response.sendRedirect(request.getRequestURL().append('/').toString()); + return; + } + + // Invoke the get operation on the service implementation + Message requestMessage = messageFactory.createMessage(); + // String id = path.substring(1); + + Message responseMessage = null; + HTTPCacheContext cacheContext = null; + try { + cacheContext = HTTPCacheContext.createCacheContextFromRequest(request); + } catch (ParseException e) { + } + + // Route message based on availability of cache info and cache methods + if (( cacheContext != null ) && (cacheContext.isEnabled()) && (conditionalPostInvoker != null )) { + requestMessage.setBody(new Object[] {cacheContext}); + responseMessage = conditionalPostInvoker.invoke(requestMessage); + } else { + requestMessage.setBody(new Object[] {}); + responseMessage = postInvoker.invoke(requestMessage); + } + if (responseMessage.isFault()) { + Object body = responseMessage.getBody(); + + int index = -1; + if ( -1 < (index = body.getClass().getName().indexOf( "NotModifiedException")) ) { + if ( index > -1 ) + response.sendError( HttpServletResponse.SC_NOT_MODIFIED, body.toString().substring( index )); + else + response.sendError( HttpServletResponse.SC_NOT_MODIFIED ); + return; + } else if ( -1 < (index = body.getClass().getName().indexOf( "PreconditionFailedException")) ) { + if ( index > -1 ) + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED, body.toString().substring( index )); + else + response.sendError( HttpServletResponse.SC_PRECONDITION_FAILED ); + return; + } + + throw new ServletException((Throwable)responseMessage.getBody()); + } + + + // Test if the ETag and LastModified are returned as a cache context. + Object body = responseMessage.getBody(); + if ( body.getClass() == HTTPCacheContext.class ) { + // Transfer to header if so. + HTTPCacheContext cc = (HTTPCacheContext)responseMessage.getBody(); + if (( cc != null ) && ( cc.isEnabled() )) { + String eTag = cc.getETag(); + if ( eTag != null ) { + response.setHeader( "ETag", cc.getETag() ); + } + String lastModified = cc.getLastModified(); + if ( lastModified != null) { + response.setHeader( "LastModified", cc.getLastModified() ); + } + } + } + } + + + public void setInvoker(Invoker invoker) { + this.invoker = invoker; + } + + public Invoker getInvoker() { + return invoker; + } + + + /** + * @return the getInvoker + */ + public Invoker getGetInvoker() { + return getInvoker; + } + + /** + * @param getInvoker the getInvoker to set + */ + public void setGetInvoker(Invoker getInvoker) { + this.getInvoker = getInvoker; + } + + /** + * @return the conditionalGetInvoker + */ + public Invoker getConditionalGetInvoker() { + return conditionalGetInvoker; + } + + /** + * @param conditionalGetInvoker the conditionalGetInvoker to set + */ + public void setConditionalGetInvoker(Invoker conditionalGetInvoker) { + this.conditionalGetInvoker = conditionalGetInvoker; + } + + /** + * @return the putInvoker + */ + public Invoker getPutInvoker() { + return putInvoker; + } + + /** + * @param putInvoker the putInvoker to set + */ + public void setPutInvoker(Invoker putInvoker) { + this.putInvoker = putInvoker; + } + + /** + * @return the conditionalPutInvoker + */ + public Invoker getConditionalPutInvoker() { + return conditionalPutInvoker; + } + + /** + * @param conditionalPutInvoker the conditionalPutInvoker to set + */ + public void setConditionalPutInvoker(Invoker conditionalPutInvoker) { + this.conditionalPutInvoker = conditionalPutInvoker; + } + + /** + * @return the postInvoker + */ + public Invoker getPostInvoker() { + return postInvoker; + } + + /** + * @param postInvoker the postInvoker to set + */ + public void setPostInvoker(Invoker postInvoker) { + this.postInvoker = postInvoker; + } + + /** + * @return the conditionalPostInvoker + */ + public Invoker getConditionalPostInvoker() { + return conditionalPostInvoker; + } + + /** + * @param conditionalPostInvoker the conditionalPostInvoker to set + */ + public void setConditionalPostInvoker(Invoker conditionalPostInvoker) { + this.conditionalPostInvoker = conditionalPostInvoker; + } + + /** + * @return the deleteInvoker + */ + public Invoker getDeleteInvoker() { + return deleteInvoker; + } + + /** + * @param deleteInvoker the deleteInvoker to set + */ + public void setDeleteInvoker(Invoker deleteInvoker) { + this.deleteInvoker = deleteInvoker; + } + + /** + * @return the conditionalDeleteInvoker + */ + public Invoker getConditionalDeleteInvoker() { + return conditionalDeleteInvoker; + } + + /** + * @param conditionalDeleteInvoker the conditionalDeleteInvoker to set + */ + public void setConditionalDeleteInvoker(Invoker conditionalDeleteInvoker) { + this.conditionalDeleteInvoker = conditionalDeleteInvoker; + } + + /** + * + * Utility methods + * + */ + + + + private void write(OutputStream out, Object obj) throws IOException { + if (obj == null) { + return; + } + if (obj instanceof String) { + out.write(((String)obj).getBytes("UTF-8")); + } else if (obj instanceof byte[]) { + out.write((byte[])obj); + } else if (obj instanceof InputStream) { + byte[] buf = new byte[8192]; + InputStream in = (InputStream)obj; + while (true) { + int size = in.read(buf); + if (size < 0) { + break; + } + out.write(buf, 0, size); + } + } else { + out.write(obj.toString().getBytes("UTF-8")); + } + } + + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java new file mode 100644 index 0000000000..0f5047deef --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java @@ -0,0 +1,63 @@ +/* + * 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 org.apache.tuscany.sca.binding.rest.RESTBinding; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.FactoryExtensionPoint; +import org.apache.tuscany.sca.host.http.ServletHost; +import org.apache.tuscany.sca.host.http.ServletHostHelper; +import org.apache.tuscany.sca.invocation.MessageFactory; +import org.apache.tuscany.sca.provider.BindingProviderFactory; +import org.apache.tuscany.sca.provider.ReferenceBindingProvider; +import org.apache.tuscany.sca.provider.ServiceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + + +/** + * Factory for REST binding providers. + * + * @version $Rev$ $Date$ + */ +public class RESTBindingProviderFactory implements BindingProviderFactory { + private ExtensionPointRegistry extensionPoints; + private MessageFactory messageFactory; + private ServletHost servletHost; + + public RESTBindingProviderFactory(ExtensionPointRegistry extensionPoints) { + this.extensionPoints = extensionPoints; + this.servletHost = ServletHostHelper.getServletHost(extensionPoints); + FactoryExtensionPoint modelFactories = extensionPoints.getExtensionPoint(FactoryExtensionPoint.class); + messageFactory = modelFactories.getFactory(MessageFactory.class); + } + + public ReferenceBindingProvider createReferenceBindingProvider(RuntimeEndpointReference endpointReference) { + return new RESTReferenceBindingProvider(extensionPoints, endpointReference); + } + + public ServiceBindingProvider createServiceBindingProvider(RuntimeEndpoint endpoint) { + return new RESTServiceBindingProvider(endpoint, extensionPoints, messageFactory, servletHost); + } + + public Class getModelType() { + return RESTBinding.class; + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java new file mode 100644 index 0000000000..e218f32573 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java @@ -0,0 +1,64 @@ +/* + * 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 org.apache.tuscany.sca.binding.rest.RESTBinding; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.provider.EndpointReferenceProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * + */ +public class RESTReferenceBindingProvider implements EndpointReferenceProvider { + private ExtensionPointRegistry registry; + private RuntimeEndpointReference endpointReference; + + public RESTReferenceBindingProvider(ExtensionPointRegistry registry, RuntimeEndpointReference endpointReference) { + super(); + this.registry = registry; + this.endpointReference = endpointReference; + } + + public void configure() { + } + + public Invoker createInvoker(Operation operation) { + return new RESTBindingInvoker(registry, (RESTBinding)endpointReference.getBinding(), operation); + } + + public InterfaceContract getBindingInterfaceContract() { + return endpointReference.getComponentReferenceInterfaceContract(); + } + + public boolean supportsOneWayInvocation() { + return false; + } + + public void start() { + } + + public void stop() { + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java new file mode 100644 index 0000000000..9a9e5a108e --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java @@ -0,0 +1,371 @@ +/* + * 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 wireFormatToMediaTypeMapping = new HashMap(); + + 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"); + } + + // Create our HTTP service listener Servlet and register it with the + // Servlet host + servletMapping = binding.getURI(); + if (!servletMapping.endsWith("/")) { + servletMapping += "/"; + } + if (!servletMapping.endsWith("*")) { + servletMapping += "*"; + } + + servletHost.addServletMapping(servletMapping, servlet); + } + + 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, application.resourceClass); + + // Create our HTTP service listener Servlet and register it with the + // Servlet host + servletMapping = binding.getURI(); + if (!servletMapping.endsWith("/")) { + servletMapping += "/"; + } + if (!servletMapping.endsWith("*")) { + servletMapping += "*"; + } + + servletHost.addServletMapping(servletMapping, 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> getClasses() { + Set> classes = new HashSet>(); + 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); + } + } + + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceListenerServlet.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceListenerServlet.java new file mode 100644 index 0000000000..1dac8a2c9a --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceListenerServlet.java @@ -0,0 +1,118 @@ +/* + * 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.io.IOException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.rest.RESTBinding; +import org.apache.tuscany.sca.common.http.HTTPCacheContext; +import org.apache.tuscany.sca.common.http.HTTPContext; +import org.apache.tuscany.sca.common.http.HTTPHeader; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.invocation.MessageFactory; + +/** + * Servlet responsible for dispatching HTTP service requests to the + * target component implementation. + * + * @version $Rev$ $Date$ + */ +public class RESTServiceListenerServlet extends HttpServlet implements Servlet { + + private static final long serialVersionUID = -5543706958107836637L; + + transient private RESTBinding binding; + transient private ServletConfig config; + transient private MessageFactory messageFactory; + transient private Invoker serviceInvoker; + + /** + * Constructs a new HTTPServiceListenerServlet. + */ + public RESTServiceListenerServlet(Binding binding, Invoker serviceInvoker, MessageFactory messageFactory) { + this.binding = (RESTBinding) binding; + this.serviceInvoker = serviceInvoker; + this.messageFactory = messageFactory; + } + + public ServletConfig getServletConfig() { + return config; + } + + public String getServletInfo() { + return ""; + } + + public void init(ServletConfig config) throws ServletException { + this.config = config; + } + + public void destroy() { + + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + HTTPContext bindingContext = new HTTPContext(); + bindingContext.setHttpRequest(request); + bindingContext.setHttpResponse(response); + + // Dispatch the service interaction to the service invoker + Message requestMessage = messageFactory.createMessage(); + requestMessage.setBindingContext(bindingContext); + requestMessage.setBody(new Object[]{request, response}); + Message responseMessage = serviceInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + // Turn a fault into an exception + //throw new ServletException((Throwable)responseMessage.getBody()); + Throwable e = (Throwable)responseMessage.getBody(); + ((HttpServletResponse)response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString()); + } else { + //handles declarative headers configured on the composite + for(HTTPHeader header : binding.getHttpHeaders()) { + //treat special headers that need to be calculated + if(header.getName().equalsIgnoreCase("Expires")) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(new Date()); + + calendar.add(Calendar.HOUR, Integer.parseInt(header.getValue())); + + response.setHeader("Expires", HTTPCacheContext.RFC822DateFormat.format( calendar.getTime() )); + } else { + //default behaviour to pass the header value to HTTP response + response.setHeader(header.getName(), header.getValue()); + } + + } + } + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java new file mode 100644 index 0000000000..23b951c88a --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java @@ -0,0 +1,127 @@ +/* + * 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.io.IOException; +import java.util.Enumeration; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.MediaType; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.wink.common.internal.registry.ProvidersRegistry; +import org.apache.wink.common.internal.registry.metadata.MethodMetadata; +import org.apache.wink.server.internal.DeploymentConfiguration; +import org.apache.wink.server.internal.RequestProcessor; +import org.apache.wink.server.internal.registry.ResourceRecord; +import org.apache.wink.server.internal.servlet.RestServlet; + +/** + * + */ +public class TuscanyRESTServlet extends RestServlet { + private static final long serialVersionUID = 89997233133964915L; + private ExtensionPointRegistry registry; + private Class resourceClass; + private boolean fixed; + + public TuscanyRESTServlet(ExtensionPointRegistry registry, Class resourceClass) { + super(); + this.registry = registry; + this.resourceClass = resourceClass; + } + + @Override + public DeploymentConfiguration getDeploymentConfiguration() throws ClassNotFoundException, InstantiationException, + IllegalAccessException, IOException { + DeploymentConfiguration config = super.getDeploymentConfiguration(); + + // [rfeng] FIXME: This is a hack to fool Apache wink to not remove the servlet path + config.setFilterConfig(new FilterConfig() { + + public ServletContext getServletContext() { + return getServletContext(); + } + + public Enumeration getInitParameterNames() { + return getInitParameterNames(); + } + + public String getInitParameter(String arg0) { + return getInitParameter(arg0); + } + + public String getFilterName() { + return getServletName(); + } + }); + + ProvidersRegistry providers = config.getProvidersRegistry(); + providers.addProvider(new DataBindingJAXRSReader(registry), 0.001, true); + providers.addProvider(new DataBindingJAXRSWriter(registry), 0.001, true); + + return config; + } + + private synchronized void fixMediaTypes(DeploymentConfiguration config) { + if (fixed) { + return; + } + // FIXME: A hacky workaround for https://issues.apache.org/jira/browse/TUSCANY-3572 + ResourceRecord record = config.getResourceRegistry().getRecord(resourceClass); + + for (MethodMetadata methodMetadata : record.getMetadata().getResourceMethods()) { + String method = methodMetadata.getHttpMethod(); + if (HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method) || HttpMethod.DELETE.equals(method)) { + methodMetadata.addConsumes(MediaType.APPLICATION_OCTET_STREAM_TYPE); + methodMetadata.addConsumes(MediaType.WILDCARD_TYPE); + } + if (HttpMethod.HEAD.equals(method) || HttpMethod.DELETE.equals(method)) { + methodMetadata.addProduces(MediaType.APPLICATION_OCTET_STREAM_TYPE); + methodMetadata.addConsumes(MediaType.WILDCARD_TYPE); + } + } + for (MethodMetadata methodMetadata : record.getMetadata().getSubResourceMethods()) { + String method = methodMetadata.getHttpMethod(); + if (HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method) || HttpMethod.DELETE.equals(method)) { + methodMetadata.addConsumes(MediaType.APPLICATION_OCTET_STREAM_TYPE); + methodMetadata.addConsumes(MediaType.WILDCARD_TYPE); + } + if (HttpMethod.HEAD.equals(method) || HttpMethod.DELETE.equals(method)) { + methodMetadata.addProduces(MediaType.APPLICATION_OCTET_STREAM_TYPE); + methodMetadata.addConsumes(MediaType.WILDCARD_TYPE); + } + } + fixed = true; + } + + @Override + public RequestProcessor getRequestProcessor() { + RequestProcessor processor = super.getRequestProcessor(); + // The 1st call returns null + if (processor != null) { + fixMediaTypes(processor.getConfiguration()); + } + return processor; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java new file mode 100644 index 0000000000..e5ce32328c --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java @@ -0,0 +1,159 @@ +/* + * 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.wireformat.json.provider; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; + +import org.apache.tuscany.sca.binding.rest.RESTBinding; +import org.apache.tuscany.sca.binding.rest.wireformat.json.JSONWireFormat; +import org.apache.tuscany.sca.common.http.HTTPContext; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; +import org.json.JSONObject; + +/** + * JSON wire format Interceptor. + * + * @version $Rev$ $Date$ +*/ +public class JSONWireFormatInterceptor implements Interceptor { + private Invoker next; + private RESTBinding binding; + + public JSONWireFormatInterceptor(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + this.binding = (RESTBinding) endpoint.getBinding(); + } + + public Invoker getNext() { + return next; + } + + public void setNext(Invoker next) { + this.next = next; + } + + public Message invoke(Message msg) { + HTTPContext bindingContext = (HTTPContext) msg.getBindingContext(); + if (bindingContext == null) { + return getNext().invoke(msg); + } + + + if (binding.getRequestWireFormat() instanceof JSONWireFormat) { + if( isPayloadSupported(bindingContext.getHttpRequest().getMethod()) && msg.getBody() != null) { + msg = invokeRequest(bindingContext, msg); + } + } + + msg = getNext().invoke(msg); + + //if it's oneway return back + Operation operation = msg.getOperation(); + if (operation != null && operation.isNonBlocking()) { + return msg; + } + + if (binding.getResponseWireFormat() instanceof JSONWireFormat) { + msg = invokeResponse(bindingContext, msg); + } + + return msg; + } + + /** + * Handle any wire format specific transformations required for request data + * @param bindingContext the binding context (e.g. HTTP Request, Response objects) + * @param msg the invocation message + * @return processed request message + */ + private Message invokeRequest(HTTPContext bindingContext, Message msg) { + + // Decode using the charset in the request if it exists otherwise + // use UTF-8 as this is what all browser implementations use. + String charset = bindingContext.getHttpRequest().getCharacterEncoding(); + if (charset == null) { + charset = "UTF-8"; + } + + try { + Object[] args = msg.getBody(); + InputStream in = (InputStream) args[0]; + String data = read(in, charset); + JSONObject jsonPayload = new JSONObject(data); + msg.setBody(new Object[]{jsonPayload}); + } catch(Exception e) { + throw new RuntimeException("Unable to parse json paylod: " + msg.getBody().toString()); + } + + return msg; + } + + /** + * Handle any wire format specific transformation required for the response data + * @param bindingContext the binding context (e.g. HTTP Request, Response objects) + * @param msg the response message + * @return processed response message + */ + private Message invokeResponse(HTTPContext bindingContext, Message msg) { + return msg; + } + + + /** + * Check if HTTP Operation should support payload + * @param operation + * @return + */ + private static boolean isPayloadSupported(String operation) { + boolean isGet = "get".equalsIgnoreCase(operation); + boolean isDelete = "delete".equalsIgnoreCase(operation); + + return isGet == false && isDelete == false; + } + + /** + * Read JSON payload from HTTP Request Body + * @param in + * @param charset + * @return + * @throws IOException + */ + private static String read(InputStream in, String charset) throws IOException { + StringWriter sw = new StringWriter(); + InputStreamReader reader = new InputStreamReader(in, "UTF-8"); + char[] buf = new char[8192]; + while (true) { + int size = reader.read(buf); + if (size < 0) { + break; + } + sw.write(buf, 0, size); + } + return sw.toString(); + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatProviderFactory.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatProviderFactory.java new file mode 100644 index 0000000000..6b1bb6cca7 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatProviderFactory.java @@ -0,0 +1,52 @@ +/* + * 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.wireformat.json.provider; + +import org.apache.tuscany.sca.binding.rest.wireformat.json.JSONWireFormat; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.provider.WireFormatProvider; +import org.apache.tuscany.sca.provider.WireFormatProviderFactory; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * JSON wire format Provider Factory. + * + * @version $Rev$ $Date$ +*/ +public class JSONWireFormatProviderFactory implements WireFormatProviderFactory{ + private ExtensionPointRegistry extensionPoints; + + public JSONWireFormatProviderFactory(ExtensionPointRegistry extensionPoints) { + this.extensionPoints = extensionPoints; + } + public WireFormatProvider createReferenceWireFormatProvider(RuntimeEndpointReference endpointReference) { + return new JSONWireFormatReferenceProvider(extensionPoints, endpointReference); + } + + public WireFormatProvider createServiceWireFormatProvider(RuntimeEndpoint endpoint) { + return new JSONWireFormatServiceProvider(extensionPoints, endpoint); + } + + public Class getModelType() { + return JSONWireFormat.class; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatReferenceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatReferenceProvider.java new file mode 100644 index 0000000000..830b02a3e8 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatReferenceProvider.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.tuscany.sca.binding.rest.wireformat.json.provider; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.WireFormatProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * JSON wire format Reference Provider. + * + * @version $Rev$ $Date$ +*/ +public class JSONWireFormatReferenceProvider implements WireFormatProvider { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpointReference endpointReference; + + public JSONWireFormatReferenceProvider(ExtensionPointRegistry extensionPoints,RuntimeEndpointReference endpointReference ) { + this.extensionPoints = extensionPoints; + this.endpointReference = endpointReference; + } + public InterfaceContract configureWireFormatInterfaceContract(InterfaceContract interfaceContract) { + return null; + } + + public Interceptor createInterceptor() { + return null; + } + + public String getPhase() { + return Phase.REFERENCE_BINDING_WIREFORMAT; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java new file mode 100644 index 0000000000..fcb311a105 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java @@ -0,0 +1,136 @@ +/* + * 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.wireformat.json.provider; + +import java.util.List; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.rest.provider.JAXRSHelper; +import org.apache.tuscany.sca.binding.rest.wireformat.json.JSONWireFormat; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.databinding.javabeans.SimpleJavaDataBinding; +import org.apache.tuscany.sca.databinding.json.JSONDataBinding; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.Interface; +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.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.WireFormatProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * JSON wire format service provider. + * + * @version $Rev$ $Date$ +*/ +public class JSONWireFormatServiceProvider implements WireFormatProvider { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpoint endpoint; + + private InterfaceContract serviceContract; + private Binding binding; + private boolean jaxrs; + + public JSONWireFormatServiceProvider(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + this.extensionPoints = extensionPoints; + this.endpoint = endpoint; + this.binding = endpoint.getBinding(); + this.jaxrs = isJAXRSResource(); + } + + private boolean isJAXRSResource() { + Interface interfaze = endpoint.getComponentServiceInterfaceContract().getInterface(); + if (interfaze instanceof JavaInterface) { + if (JAXRSHelper.isJAXRSResource(((JavaInterface)interfaze).getJavaClass())) { + return true; + } + } + return false; + } + + public InterfaceContract configureWireFormatInterfaceContract(InterfaceContract interfaceContract) { + serviceContract = interfaceContract; + + if (!jaxrs) { + boolean configureInput = binding.getRequestWireFormat() != null; + boolean configureOutput = binding.getRequestWireFormat() != null || binding.getResponseWireFormat() != null; + + //set JSON databinding + setDataBinding(serviceContract.getInterface(), configureInput, configureOutput); + } + + return serviceContract; + } + + public Interceptor createInterceptor() { + if (jaxrs) { + return null; + } + if( (binding.getRequestWireFormat() != null && binding.getRequestWireFormat() instanceof JSONWireFormat) || + (binding.getResponseWireFormat() != null && binding.getResponseWireFormat() instanceof JSONWireFormat) ){ + return new JSONWireFormatInterceptor(extensionPoints, endpoint); + } + return null; + } + + public String getPhase() { + return Phase.SERVICE_BINDING_WIREFORMAT; + } + + + /** + * Utility method to reset data binding for the interface contract. + * We need to handle special case where : + * - global wire format for binding + * - wire format configured only for response + * @param interfaze + */ + @SuppressWarnings({"deprecation", "unchecked"}) + private void setDataBinding(Interface interfaze, boolean configureInput, boolean configureOutput) { + List operations = interfaze.getOperations(); + for (Operation operation : operations) { + // handle input types + if (configureInput) { + operation.setDataBinding(JSONDataBinding.NAME); + DataType> inputType = operation.getInputType(); + if (inputType != null) { + List logical = inputType.getLogical(); + for (DataType inArg : logical) { + if (!SimpleJavaDataBinding.NAME.equals(inArg.getDataBinding())) { + inArg.setDataBinding(JSONDataBinding.NAME); + } + } + } + } + + // handle output types + if (configureOutput) { + DataType outputType = operation.getOutputType(); + if (outputType != null) { + if (!SimpleJavaDataBinding.NAME.equals(outputType.getDataBinding())) { + outputType.setDataBinding(JSONDataBinding.NAME); + } + } + } + } + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatInterceptor.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatInterceptor.java new file mode 100644 index 0000000000..d5055b6701 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatInterceptor.java @@ -0,0 +1,143 @@ +/* + * 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.wireformat.xml.provider; + +import java.io.InputStream; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; + +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.common.http.HTTPContext; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.FactoryExtensionPoint; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * JSON wire format Interceptor. + * + * @version $Rev$ $Date$ +*/ +public class XMLWireFormatInterceptor implements Interceptor { + private XMLInputFactory inputFactory; + + private Invoker next; + private RESTBinding binding; + + public XMLWireFormatInterceptor(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + FactoryExtensionPoint factories = extensionPoints.getExtensionPoint(FactoryExtensionPoint.class); + inputFactory = factories.getFactory(XMLInputFactory.class); + + this.binding = (RESTBinding) endpoint.getBinding(); + } + + public Invoker getNext() { + return next; + } + + public void setNext(Invoker next) { + this.next = next; + } + + public Message invoke(Message msg) { + HTTPContext bindingContext = (HTTPContext) msg.getBindingContext(); + if (bindingContext == null) { + return getNext().invoke(msg); + } + + + if (binding.getRequestWireFormat() instanceof XMLWireFormat) { + if( isPayloadSupported(bindingContext.getHttpRequest().getMethod()) && msg.getBody() != null) { + msg = invokeRequest(bindingContext, msg); + } + } + + msg = getNext().invoke(msg); + + //if it's oneway return back + Operation operation = msg.getOperation(); + if (operation != null && operation.isNonBlocking()) { + return msg; + } + + if (binding.getResponseWireFormat() instanceof XMLWireFormat) { + msg = invokeResponse(bindingContext, msg); + } + + return msg; + } + + /** + * Handle any wire format specific transformations required for request data + * @param bindingContext the binding context (e.g. HTTP Request, Response objects) + * @param msg the invocation message + * @return processed request message + */ + private Message invokeRequest(HTTPContext bindingContext, Message msg) { + // Decode using the charset in the request if it exists otherwise + // use UTF-8 as this is what all browser implementations use. + String charset = bindingContext.getHttpRequest().getCharacterEncoding(); + if (charset == null) { + charset = "UTF-8"; + } + + try { + if(msg.getBody() != null) { + Object[] args = msg.getBody(); + InputStream data = (InputStream) args[0]; + XMLStreamReader xmlPayload = inputFactory.createXMLStreamReader(data, charset); + msg.setBody(new Object[]{xmlPayload}); + } + } catch(Exception e) { + throw new RuntimeException("Unable to process xml paylod: " + msg.getBody().toString()); + } + + return msg; + } + + + /** + * Handle any wire format specific transformation required for the response data + * @param bindingContext the binding context (e.g. HTTP Request, Response objects) + * @param msg the response message + * @return processed response message + */ + private Message invokeResponse(HTTPContext bindingContext, Message msg) { + return msg; + } + + /** + * Check if HTTP Operation should support payload + * @param operation + * @return + */ + private static boolean isPayloadSupported(String operation) { + boolean isGet = "get".equalsIgnoreCase(operation); + boolean isDelete = "delete".equalsIgnoreCase(operation); + + return isGet == false && isDelete == false; + } +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatProviderFactory.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatProviderFactory.java new file mode 100644 index 0000000000..33900b3de9 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatProviderFactory.java @@ -0,0 +1,52 @@ +/* + * 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.wireformat.xml.provider; + +import org.apache.tuscany.sca.binding.rest.wireformat.xml.XMLWireFormat; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.provider.WireFormatProvider; +import org.apache.tuscany.sca.provider.WireFormatProviderFactory; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * XML wire format Provider Factory. + * + * @version $Rev$ $Date$ +*/ +public class XMLWireFormatProviderFactory implements WireFormatProviderFactory{ + private ExtensionPointRegistry extensionPoints; + + public XMLWireFormatProviderFactory(ExtensionPointRegistry extensionPoints) { + this.extensionPoints = extensionPoints; + } + public WireFormatProvider createReferenceWireFormatProvider(RuntimeEndpointReference endpointReference) { + return new XMLWireFormatReferenceProvider(extensionPoints, endpointReference); + } + + public WireFormatProvider createServiceWireFormatProvider(RuntimeEndpoint endpoint) { + return new XMLWireFormatServiceProvider(extensionPoints, endpoint); + } + + public Class getModelType() { + return XMLWireFormat.class; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatReferenceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatReferenceProvider.java new file mode 100644 index 0000000000..c9e0ac4578 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatReferenceProvider.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.tuscany.sca.binding.rest.wireformat.xml.provider; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.WireFormatProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * XML wire format Reference Provider. + * + * @version $Rev$ $Date$ +*/ +public class XMLWireFormatReferenceProvider implements WireFormatProvider { + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpointReference endpointReference; + + public XMLWireFormatReferenceProvider(ExtensionPointRegistry extensionPoints,RuntimeEndpointReference endpointReference ) { + this.extensionPoints = extensionPoints; + this.endpointReference = endpointReference; + } + public InterfaceContract configureWireFormatInterfaceContract(InterfaceContract interfaceContract) { + return null; + } + + public Interceptor createInterceptor() { + return null; + } + + public String getPhase() { + return Phase.REFERENCE_BINDING_WIREFORMAT; + } + +} diff --git a/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java new file mode 100644 index 0000000000..32d718b509 --- /dev/null +++ b/sandbox/sebastien/java/extend/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java @@ -0,0 +1,137 @@ +/* + * 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.wireformat.xml.provider; + +import java.util.List; + +import javax.xml.stream.XMLStreamReader; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.rest.provider.JAXRSHelper; +import org.apache.tuscany.sca.binding.rest.wireformat.xml.XMLWireFormat; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.databinding.javabeans.SimpleJavaDataBinding; +import org.apache.tuscany.sca.databinding.xml.XMLStringDataBinding; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.Interface; +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.invocation.Interceptor; +import org.apache.tuscany.sca.invocation.Phase; +import org.apache.tuscany.sca.provider.WireFormatProvider; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * XML wire format service provider. + * + * @version $Rev$ $Date$ +*/ +public class XMLWireFormatServiceProvider implements WireFormatProvider { + private static final String DATABABINDING = XMLStreamReader.class.getName(); + + private ExtensionPointRegistry extensionPoints; + private RuntimeEndpoint endpoint; + + private InterfaceContract serviceContract; + private Binding binding; + + private boolean jaxrs; + + public XMLWireFormatServiceProvider(ExtensionPointRegistry extensionPoints, RuntimeEndpoint endpoint) { + this.extensionPoints = extensionPoints; + this.endpoint = endpoint; + this.binding = endpoint.getBinding(); + this.jaxrs = isJAXRSResource(); + } + + private boolean isJAXRSResource() { + Interface interfaze = endpoint.getComponentServiceInterfaceContract().getInterface(); + if (interfaze instanceof JavaInterface) { + if (JAXRSHelper.isJAXRSResource(((JavaInterface)interfaze).getJavaClass())) { + return true; + } + } + return false; + } + + public InterfaceContract configureWireFormatInterfaceContract(InterfaceContract interfaceContract) { + serviceContract = interfaceContract; + + if (!jaxrs) { + boolean configureInput = binding.getRequestWireFormat() != null; + boolean configureOutput = binding.getRequestWireFormat() != null || binding.getResponseWireFormat() != null; + + //set XML databinding + setDataBinding(serviceContract.getInterface(), configureInput, configureOutput); + } + + return serviceContract; + } + + public Interceptor createInterceptor() { + if (jaxrs) { + return null; + } + + if( (binding.getRequestWireFormat() != null && binding.getRequestWireFormat() instanceof XMLWireFormat) || + (binding.getResponseWireFormat() != null && binding.getResponseWireFormat() instanceof XMLWireFormat) ){ + return new XMLWireFormatInterceptor(extensionPoints, endpoint); + } + return null; + } + + public String getPhase() { + return Phase.SERVICE_BINDING_WIREFORMAT; + } + + + /** + * Utility method to reset data binding for the interface contract + * @param interfaze + */ + @SuppressWarnings({"deprecation", "unchecked"}) + private void setDataBinding(Interface interfaze, boolean configureInput, boolean configureOutput) { + List operations = interfaze.getOperations(); + for (Operation operation : operations) { + // handle input types + if (configureInput) { + operation.setDataBinding(DATABABINDING); + DataType> inputType = operation.getInputType(); + if (inputType != null) { + List logical = inputType.getLogical(); + for (DataType inArg : logical) { + if (!SimpleJavaDataBinding.NAME.equals(inArg.getDataBinding())) { + inArg.setDataBinding(DATABABINDING); + } + } + } + } + + // handle output types + if (configureOutput) { + DataType outputType = operation.getOutputType(); + if (outputType != null) { + outputType.setDataBinding(XMLStringDataBinding.NAME); + } + } + } + } +} -- cgit v1.2.3