summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider')
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java203
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java88
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java89
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/JAXRSHelper.java80
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java298
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java583
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java63
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java64
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java375
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceListenerServlet.java118
-rw-r--r--sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java220
11 files changed, 2181 insertions, 0 deletions
diff --git a/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java b/sandbox/sebastien/java/wrapped/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/wrapped/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 extends Annotation> A getAnnotation(Annotation[] annotations, Class<A> 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/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java b/sandbox/sebastien/java/wrapped/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/wrapped/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<T> extends DataBindingJAXRSProvider implements MessageBodyReader<T> {
+
+ 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<T> type,
+ Type genericType,
+ Annotation[] annotations,
+ MediaType mediaType,
+ MultivaluedMap<String, String> 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.<String, Object>emptyMap());
+ return (T)result;
+ }
+
+
+
+}
diff --git a/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java b/sandbox/sebastien/java/wrapped/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/wrapped/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<T> extends DataBindingJAXRSProvider implements MessageBodyWriter<T> {
+
+ 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<String, Object> 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.<String, Object> emptyMap());
+ }
+
+}
diff --git a/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/JAXRSHelper.java b/sandbox/sebastien/java/wrapped/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/wrapped/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/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java b/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java
new file mode 100644
index 0000000000..2463883b80
--- /dev/null
+++ b/sandbox/sebastien/java/wrapped/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<Class<?>, String> mapping = new HashMap<Class<?>, 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 extends Annotation> T getAnnotation(Annotation[] annotations, Class<T> 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<Class<?>> getClasses() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<Object> getSingletons() {
+ Set<Object> providers = new HashSet<Object>();
+ 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<Class<?>, String> e : mapping.entrySet()) {
+ if (operation.getAttributes().get(e.getKey()) != null) {
+ httpMethod = e.getValue();
+ break;
+ }
+ }
+
+ if (operation.getOutputType() != null) {
+ responseType = operation.getOutputType().getLogical().get(0).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<String, Object> pathParams = new HashMap<String, Object>();
+ Map<String, Object> matrixParams = new HashMap<String, Object>();
+ Map<String, Object> queryParams = new HashMap<String, Object>();
+ Map<String, Object> headerParams = new HashMap<String, Object>();
+ Map<String, Object> formParams = new HashMap<String, Object>();
+ Map<String, Object> cookieParams = new HashMap<String, Object>();
+
+ 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<String, Object> p : queryParams.entrySet()) {
+ uriBuilder.replaceQueryParam(p.getKey(), p.getValue());
+ }
+ for (Map.Entry<String, Object> p : matrixParams.entrySet()) {
+ uriBuilder.replaceMatrixParam(p.getKey(), p.getValue());
+ }
+
+ uri = uriBuilder.buildFromMap(pathParams);
+ Resource resource = restClient.resource(uri);
+
+ for (Map.Entry<String, Object> p : headerParams.entrySet()) {
+ resource.header(p.getKey(), String.valueOf(p.getValue()));
+ }
+
+ for (Map.Entry<String, Object> 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/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java b/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java
new file mode 100644
index 0000000000..e0a681ed36
--- /dev/null
+++ b/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingListenerServlet.java
@@ -0,0 +1,583 @@
+/*
+ * 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 pathInfo = request.getPathInfo();
+ if (pathInfo == null || pathInfo.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;
+ }
+ String path = URLDecoder.decode(pathInfo, "UTF-8");
+
+ // 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/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java b/sandbox/sebastien/java/wrapped/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/wrapped/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<RESTBinding> {
+ 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<RESTBinding> getModelType() {
+ return RESTBinding.class;
+ }
+}
diff --git a/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java b/sandbox/sebastien/java/wrapped/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/wrapped/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/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java b/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java
new file mode 100644
index 0000000000..fab971de53
--- /dev/null
+++ b/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.binding.rest.provider;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Servlet;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.xml.namespace.QName;
+
+import org.apache.tuscany.sca.binding.rest.RESTBinding;
+import org.apache.tuscany.sca.binding.rest.wireformat.json.JSONWireFormat;
+import org.apache.tuscany.sca.binding.rest.wireformat.xml.XMLWireFormat;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.invocation.ExtensibleProxyFactory;
+import org.apache.tuscany.sca.core.invocation.ProxyFactory;
+import org.apache.tuscany.sca.host.http.ServletHost;
+import org.apache.tuscany.sca.interfacedef.InterfaceContract;
+import org.apache.tuscany.sca.interfacedef.Operation;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.apache.tuscany.sca.interfacedef.java.jaxrs.RootResourceClassGenerator;
+import org.apache.tuscany.sca.invocation.Interceptor;
+import org.apache.tuscany.sca.invocation.InvocationChain;
+import org.apache.tuscany.sca.invocation.Invoker;
+import org.apache.tuscany.sca.invocation.MessageFactory;
+import org.apache.tuscany.sca.invocation.Phase;
+import org.apache.tuscany.sca.provider.EndpointProvider;
+import org.apache.tuscany.sca.provider.OperationSelectorProvider;
+import org.apache.tuscany.sca.provider.OperationSelectorProviderFactory;
+import org.apache.tuscany.sca.provider.ProviderFactoryExtensionPoint;
+import org.apache.tuscany.sca.provider.WireFormatProvider;
+import org.apache.tuscany.sca.provider.WireFormatProviderFactory;
+import org.apache.tuscany.sca.runtime.RuntimeComponent;
+import org.apache.tuscany.sca.runtime.RuntimeComponentService;
+import org.apache.tuscany.sca.runtime.RuntimeEndpoint;
+import org.apache.wink.server.utils.RegistrationUtils;
+import org.oasisopen.sca.ServiceRuntimeException;
+
+/**
+ * Implementation of an HTTP binding provider.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RESTServiceBindingProvider implements EndpointProvider {
+ private static Map<QName, String> wireFormatToMediaTypeMapping = new HashMap<QName, String>();
+
+ static {
+ wireFormatToMediaTypeMapping.put(JSONWireFormat.REST_WIREFORMAT_JSON_QNAME, MediaType.APPLICATION_JSON);
+ wireFormatToMediaTypeMapping.put(XMLWireFormat.REST_WIREFORMAT_XML_QNAME, MediaType.APPLICATION_XML);
+ }
+
+ private ExtensionPointRegistry extensionPoints;
+
+ private RuntimeEndpoint endpoint;
+ private RuntimeComponent component;
+ private RuntimeComponentService service;
+ private InterfaceContract serviceContract;
+ private RESTBinding binding;
+ private MessageFactory messageFactory;
+
+ private OperationSelectorProvider osProvider;
+ private WireFormatProvider wfProvider;
+ private WireFormatProvider wfResponseProvider;
+
+ private ServletHost servletHost;
+ private String servletMapping;
+ private RESTBindingListenerServlet bindingListenerServlet;
+
+ private SimpleApplication application;
+
+ public RESTServiceBindingProvider(RuntimeEndpoint endpoint,
+ ExtensionPointRegistry extensionPoints,
+ MessageFactory messageFactory,
+ ServletHost servletHost) {
+
+ this.endpoint = endpoint;
+ this.component = (RuntimeComponent)endpoint.getComponent();
+ this.service = (RuntimeComponentService)endpoint.getService();
+ this.binding = (RESTBinding)endpoint.getBinding();
+
+ this.extensionPoints = extensionPoints;
+ this.messageFactory = messageFactory;
+ this.servletHost = servletHost;
+
+ // retrieve operation selector and wire format service providers
+
+ ProviderFactoryExtensionPoint providerFactories =
+ extensionPoints.getExtensionPoint(ProviderFactoryExtensionPoint.class);
+
+ if (binding.getOperationSelector() != null) {
+ // Configure the interceptors for operation selection
+ OperationSelectorProviderFactory osProviderFactory =
+ (OperationSelectorProviderFactory)providerFactories.getProviderFactory(binding.getOperationSelector()
+ .getClass());
+ if (osProviderFactory != null) {
+ this.osProvider = osProviderFactory.createServiceOperationSelectorProvider(endpoint);
+ }
+ }
+
+ if (binding.getRequestWireFormat() != null) {
+ // Configure the interceptors for wire format
+ WireFormatProviderFactory wfProviderFactory =
+ (WireFormatProviderFactory)providerFactories.getProviderFactory(binding.getRequestWireFormat()
+ .getClass());
+ if (wfProviderFactory != null) {
+ this.wfProvider = wfProviderFactory.createServiceWireFormatProvider(endpoint);
+ }
+ }
+
+ if (binding.getResponseWireFormat() != null) {
+ // Configure the interceptors for wire format
+ WireFormatProviderFactory wfProviderFactory =
+ (WireFormatProviderFactory)providerFactories.getProviderFactory(binding.getResponseWireFormat()
+ .getClass());
+ if (wfProviderFactory != null) {
+ this.wfResponseProvider = wfProviderFactory.createServiceWireFormatProvider(endpoint);
+ }
+ }
+
+ //clone the service contract to avoid databinding issues
+ try {
+ this.serviceContract = (InterfaceContract)service.getInterfaceContract().clone();
+
+ // configure data binding
+ if (wfProvider != null) {
+ wfProvider.configureWireFormatInterfaceContract(serviceContract);
+ }
+
+ if (wfResponseProvider != null) {
+ wfResponseProvider.configureWireFormatInterfaceContract(serviceContract);
+ }
+ } catch (CloneNotSupportedException e) {
+ this.serviceContract = service.getInterfaceContract();
+ }
+
+ }
+
+ public InterfaceContract getBindingInterfaceContract() {
+ return serviceContract;
+ }
+
+
+ /**
+ * 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);
+ }
+ }
+
+ }
+
+ public void start() {
+ InvocationChain bindingChain = endpoint.getBindingInvocationChain();
+
+ application = registerWithJAXRS();
+ if (application != null) {
+ return;
+ }
+
+ // Get the invokers for the supported operations
+ Servlet servlet = null;
+ Invoker bindingInvoker = bindingChain.getHeadInvoker();
+ bindingListenerServlet = new RESTBindingListenerServlet(binding, bindingInvoker, messageFactory);
+ for (InvocationChain invocationChain : endpoint.getInvocationChains()) {
+
+ Operation operation = invocationChain.getTargetOperation();
+ Invoker serviceInvoker = invocationChain.getHeadInvoker();
+ String operationName = operation.getName();
+
+ if (binding.getOperationSelector() != null || binding.getRequestWireFormat() != null) {
+ bindingListenerServlet.setInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("get")) {
+ bindingListenerServlet.setGetInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("conditionalGet")) {
+ bindingListenerServlet.setConditionalGetInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("delete")) {
+ bindingListenerServlet.setDeleteInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("conditionalDelete")) {
+ bindingListenerServlet.setConditionalDeleteInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("put")) {
+ bindingListenerServlet.setPutInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("conditionalPut")) {
+ bindingListenerServlet.setConditionalPutInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("post")) {
+ bindingListenerServlet.setPostInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("conditionalPost")) {
+ bindingListenerServlet.setConditionalPostInvoker(serviceInvoker);
+ servlet = bindingListenerServlet;
+ } else if (operationName.equals("service")) {
+ servlet = new RESTServiceListenerServlet(binding, serviceInvoker, messageFactory);
+ break;
+ }
+ }
+ if (servlet == null) {
+ throw new IllegalStateException("No get or service method found on the service");
+ }
+
+ servletMapping = registerServlet(servlet);
+ }
+
+ public void stop() {
+ if (application != null) {
+ application.destroy();
+ }
+ // Unregister the Servlet from the Servlet host
+ servletHost.removeServletMapping(servletMapping);
+ }
+
+
+ public boolean supportsOneWayInvocation() {
+ return false;
+ }
+
+
+ private String registerServlet(Servlet servlet) {
+ // Create our HTTP service listener Servlet and register it with the
+ // Servlet host
+ String servletMapping = binding.getURI();
+ if (!servletMapping.endsWith("/")) {
+ servletMapping += "/";
+ }
+ if (!servletMapping.endsWith("*")) {
+ servletMapping += "*";
+ }
+
+ String mappedURI = servletHost.addServletMapping(servletMapping, servlet);
+ String deployedURI = mappedURI;
+ if (deployedURI.endsWith("*")) {
+ deployedURI = deployedURI.substring(0, deployedURI.length() - 1);
+ }
+ binding.setURI(deployedURI);
+ return mappedURI;
+ }
+
+ /**
+ * Register a Tuscany REST Servlet to handle JAX-RS Resources on a binding endpoint
+ * @return
+ */
+ private SimpleApplication registerWithJAXRS() {
+ try {
+ SimpleApplication application = null;
+
+ JavaInterface javaInterface = (JavaInterface)endpoint.getComponentServiceInterfaceContract().getInterface();
+ Class<?> interfaze = javaInterface.getJavaClass();
+
+ // The @Path annotation can be from the binding uri
+ boolean isJAXRS = JAXRSHelper.isJAXRSResource(interfaze);
+ if (isJAXRS) {
+ application = new SimpleApplication(interfaze);
+
+ TuscanyRESTServlet restServlet = new TuscanyRESTServlet(extensionPoints, binding, application.resourceClass);
+
+ servletMapping = registerServlet(restServlet);
+
+ RegistrationUtils.registerApplication(application, restServlet.getServletContext());
+ return application;
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ throw new ServiceRuntimeException(e);
+ }
+ }
+
+ private class SimpleApplication extends Application {
+ private Class<?> resourceClass;
+
+ public SimpleApplication(Class<?> resourceClass) {
+ super();
+ if (resourceClass.isInterface()) {
+ this.resourceClass = generateResourceClass(resourceClass);
+ } else {
+ this.resourceClass = resourceClass;
+ }
+ }
+
+ @Override
+ public Set<Class<?>> getClasses() {
+ Set<Class<?>> classes = new HashSet<Class<?>>();
+ classes.add(resourceClass);
+ return classes;
+ }
+
+ private Class<?> generateResourceClass(Class<?> interfaze) {
+ try {
+ QName requestWireFormat = null;
+ if (binding.getRequestWireFormat() != null) {
+ requestWireFormat = binding.getRequestWireFormat().getSchemaName();
+ }
+ QName responeWireFormat = null;
+ if (binding.getResponseWireFormat() != null) {
+ responeWireFormat = binding.getRequestWireFormat().getSchemaName();
+ }
+ String requestMediaType = wireFormatToMediaTypeMapping.get(requestWireFormat);
+ String responseMediaType = wireFormatToMediaTypeMapping.get(responeWireFormat);
+
+ String uri = endpoint.getBinding().getURI();
+ String path = URI.create(uri).getPath();
+
+ // FIXME: [rfeng] We need to have a better way to deal with URI template for bindings
+ if (path.startsWith(servletHost.getContextPath())) {
+ path = path.substring(servletHost.getContextPath().length());
+ }
+ Class<?> cls =
+ RootResourceClassGenerator.generateRootResourceClass(interfaze,
+ path,
+ requestMediaType,
+ responseMediaType);
+ ProxyFactory proxyFactory = ExtensibleProxyFactory.getInstance(extensionPoints);
+ Object proxy = proxyFactory.createProxy(interfaze, endpoint);
+ RootResourceClassGenerator.injectProxy(cls, proxy);
+ return cls;
+ } catch (Exception e) {
+ throw new ServiceRuntimeException(e);
+ }
+ }
+
+ public void destroy() {
+ resourceClass = null;
+ }
+ }
+
+}
diff --git a/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceListenerServlet.java b/sandbox/sebastien/java/wrapped/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/wrapped/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/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java b/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java
new file mode 100644
index 0000000000..061d615d3e
--- /dev/null
+++ b/sandbox/sebastien/java/wrapped/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java
@@ -0,0 +1,220 @@
+/*
+ * 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.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+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.HTTPHeader;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.extensibility.ClassLoaderContext;
+import org.apache.wink.common.internal.registry.ProvidersRegistry;
+import org.apache.wink.server.handlers.HandlersChain;
+import org.apache.wink.server.handlers.MessageContext;
+import org.apache.wink.server.handlers.ResponseHandler;
+import org.apache.wink.server.internal.DeploymentConfiguration;
+import org.apache.wink.server.internal.servlet.RestServlet;
+
+/**
+ *
+ */
+public class TuscanyRESTServlet extends RestServlet {
+ private static final Logger logger = Logger.getLogger(TuscanyRESTServlet.class.getName());
+
+ private static final long serialVersionUID = 89997233133964915L;
+ private ExtensionPointRegistry registry;
+ private RESTBinding binding;
+ private Class<?> resourceClass;
+ private boolean fixed;
+
+ public TuscanyRESTServlet(ExtensionPointRegistry registry, Binding binding, Class<?> resourceClass) {
+ super();
+ this.registry = registry;
+ this.binding = (RESTBinding) binding;
+ this.resourceClass = resourceClass;
+ }
+
+ public void init() throws ServletException {
+ ClassLoader cl =
+ ClassLoaderContext.setContextClassLoader(Thread.currentThread().getContextClassLoader(),
+ registry.getServiceDiscovery(),
+ "/META-INF/server/wink-providers",
+ "javax.ws.rs.ext.RuntimeDelegate");
+ try {
+ super.init();
+ } finally {
+ if (cl != null) {
+ // return previous classLoader
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ @Override
+ public DeploymentConfiguration getDeploymentConfiguration() throws ClassNotFoundException, InstantiationException,
+ IllegalAccessException, IOException {
+
+
+ // setup proper classLoader to work on OSGi environment
+ ClassLoader cl =
+ ClassLoaderContext.setContextClassLoader(Thread.currentThread().getContextClassLoader(),
+ registry.getServiceDiscovery(),
+ "javax.ws.rs.ext.RuntimeDelegate",
+ "/META-INF/wink-alternate-shortcuts.properties",
+ "/META-INF/server/wink-providers");
+
+ DeploymentConfiguration config = null;
+ try {
+ config = super.getDeploymentConfiguration();
+ } finally {
+ if (cl != null) {
+ // return previous classLoader
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+ // [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);
+
+ config.getResponseUserHandlers().add(new TuscanyResponseHandler());
+
+ 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;
+ }
+ */
+
+ /**
+ * TuscanyResponseHandler
+ *
+ * Required to support declartive HTTP Headers
+ */
+ class TuscanyResponseHandler implements ResponseHandler {
+ public void handleResponse(MessageContext context, HandlersChain chain) throws Throwable {
+
+ // assert response is not committed
+ final HttpServletResponse httpResponse = context.getAttribute(HttpServletResponse.class);
+ if (httpResponse.isCommitted()) {
+ logger.log(Level.FINE, "The response is already committed. Nothing to do.");
+ return;
+ }
+
+ //process declarative headers
+ 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()));
+
+ httpResponse.setHeader("Expires", HTTPCacheContext.RFC822DateFormat.format( calendar.getTime() ));
+ } else {
+ //default behaviour to pass the header value to HTTP response
+ httpResponse.setHeader(header.getName(), header.getValue());
+ }
+ }
+
+ chain.doChain(context);
+ }
+
+ public void init(Properties props) {
+
+ }
+ }
+
+}