diff options
Diffstat (limited to 'sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java')
12 files changed, 898 insertions, 53 deletions
diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java index a79cd7dcf9..928b80a044 100644 --- a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/operationselector/jaxrs/provider/JAXRSOperationSelectorInterceptor.java @@ -83,6 +83,11 @@ public class JAXRSOperationSelectorInterceptor implements Interceptor { 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"); diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java new file mode 100644 index 0000000000..d7eeede240 --- /dev/null +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSProvider.java @@ -0,0 +1,201 @@ +/* + * 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) { + return MediaType.APPLICATION_JSON_TYPE.equals(mediaType) || MediaType.APPLICATION_XML_TYPE.equals(mediaType) + || MediaType.TEXT_XML_TYPE.equals(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/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java new file mode 100644 index 0000000000..035af0aedf --- /dev/null +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSReader.java @@ -0,0 +1,84 @@ +/* + * 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; + + // 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 (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/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java new file mode 100644 index 0000000000..8503d089eb --- /dev/null +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/DataBindingJAXRSWriter.java @@ -0,0 +1,86 @@ +/* + * 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); + + 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 { + 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/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java new file mode 100644 index 0000000000..72a0c1a56d --- /dev/null +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingInvoker.java @@ -0,0 +1,103 @@ +/* + * 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.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; + +import org.apache.tuscany.sca.binding.rest.RESTBinding; +import org.apache.tuscany.sca.interfacedef.Operation; +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.EntityType; +import org.apache.wink.client.Resource; +import org.apache.wink.client.RestClient; + +/** + * + */ +public class RESTBindingInvoker implements Invoker { + private RESTBinding binding; + private Operation operation; + + public RESTBindingInvoker(RESTBinding binding, Operation operation) { + super(); + this.binding = binding; + this.operation = operation; + } + + 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); + } + + public Message invoke(Message msg) { + ClientConfig config = new ClientConfig(); + RestClient client = new RestClient(); + Resource resource = client.resource(binding.getURI()); + String method = null; + for (Map.Entry<Class<?>, String> e : mapping.entrySet()) { + if (operation.getAttributes().get(e.getKey()) != null) { + method = e.getValue(); + break; + } + } + EntityType<?> entityType = new EntityType() { + + @Override + public Class getRawClass() { + if (operation.getOutputType() != null) { + return operation.getOutputType().getPhysical(); + } else { + return null; + } + } + + @Override + public Type getType() { + if (operation.getOutputType() != null) { + return operation.getOutputType().getGenericType(); + } else { + return null; + } + } + + }; + Object result = resource.invoke(method, entityType, msg.getBody()); + msg.setBody(result); + return msg; + } + +} diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java index 23012f4a6b..7c9ec85a9e 100644 --- a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTBindingProviderFactory.java @@ -50,8 +50,9 @@ public class RESTBindingProviderFactory implements BindingProviderFactory<RESTBi } public ReferenceBindingProvider createReferenceBindingProvider(RuntimeEndpointReference endpointReference) { + return new RESTReferenceBindingProvider(extensionPoints, endpointReference); // Binding REST is currently NOT supporting References - return null; + // throw new UnsupportedOperationException("binding.rest for SCA references is not implemented yet."); } public ServiceBindingProvider createServiceBindingProvider(RuntimeEndpoint endpoint) { diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTReferenceBindingProvider.java new file mode 100644 index 0000000000..06977fd2f8 --- /dev/null +++ b/sca-java-2.x/trunk/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((RESTBinding)endpointReference.getBinding(), operation); + } + + public InterfaceContract getBindingInterfaceContract() { + return endpointReference.getComponentReferenceInterfaceContract(); + } + + public boolean supportsOneWayInvocation() { + return false; + } + + public void start() { + } + + public void stop() { + } + +} diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java index 514cb4cc96..fdabb13ce5 100644 --- a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/RESTServiceBindingProvider.java @@ -19,13 +19,31 @@ 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.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; @@ -39,6 +57,8 @@ 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. @@ -47,86 +67,98 @@ import org.apache.tuscany.sca.runtime.RuntimeEndpoint; */ public class RESTServiceBindingProvider implements EndpointProvider { private ExtensionPointRegistry extensionPoints; - + private RuntimeEndpoint endpoint; private RuntimeComponent component; - private RuntimeComponentService service; + private RuntimeComponentService service; private InterfaceContract serviceContract; private RESTBinding binding; private MessageFactory messageFactory; - + private OperationSelectorProvider osProvider; private WireFormatProvider wfProvider; - + 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.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); - + ProviderFactoryExtensionPoint providerFactories = + extensionPoints.getExtensionPoint(ProviderFactoryExtensionPoint.class); + if (binding.getOperationSelector() != null) { // Configure the interceptors for operation selection - OperationSelectorProviderFactory osProviderFactory = (OperationSelectorProviderFactory) providerFactories.getProviderFactory(binding.getOperationSelector().getClass()); + OperationSelectorProviderFactory osProviderFactory = + (OperationSelectorProviderFactory)providerFactories.getProviderFactory(binding.getOperationSelector() + .getClass()); if (osProviderFactory != null) { this.osProvider = osProviderFactory.createServiceOperationSelectorProvider(endpoint); - } + } } - + if (binding.getRequestWireFormat() != null && binding.getResponseWireFormat() != null) { // Configure the interceptors for wire format - WireFormatProviderFactory wfProviderFactory = (WireFormatProviderFactory) providerFactories.getProviderFactory(binding.getRequestWireFormat().getClass()); + WireFormatProviderFactory wfProviderFactory = + (WireFormatProviderFactory)providerFactories.getProviderFactory(binding.getRequestWireFormat() + .getClass()); if (wfProviderFactory != null) { this.wfProvider = wfProviderFactory.createServiceWireFormatProvider(endpoint); - } + } } - //clone the service contract to avoid databinding issues try { - this.serviceContract = (InterfaceContract) service.getInterfaceContract().clone(); - + this.serviceContract = (InterfaceContract)service.getInterfaceContract().clone(); + // configure data binding if (this.wfProvider != null) { wfProvider.configureWireFormatInterfaceContract(serviceContract); } - } catch(CloneNotSupportedException e) { + } 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 = endpoint.getBindingInvocationChain().getHeadInvoker(); - bindingListenerServlet = new RESTBindingListenerServlet(binding, bindingInvoker, messageFactory ); + 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")) { + } else if (operationName.equals("get")) { bindingListenerServlet.setGetInvoker(serviceInvoker); servlet = bindingListenerServlet; } else if (operationName.equals("conditionalGet")) { @@ -153,12 +185,12 @@ public class RESTServiceBindingProvider implements EndpointProvider { } 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(); @@ -168,11 +200,139 @@ public class RESTServiceBindingProvider implements EndpointProvider { if (!servletMapping.endsWith("*")) { servletMapping += "*"; } - + servletHost.addServletMapping(servletMapping, servlet); } - public void stop() { + 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 SimpleApplication registerWithJAXRS() { + try { + SimpleApplication application = null; + + JavaInterface javaInterface = (JavaInterface)endpoint.getComponentServiceInterfaceContract().getInterface(); + Class<?> interfaze = javaInterface.getJavaClass(); + + boolean isJAXRS = isJAXRSResource(interfaze); + if (isJAXRS) { + application = new SimpleApplication(interfaze); + + TuscanyRESTServlet restServlet = new TuscanyRESTServlet(extensionPoints); + + // 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(); + // boolean isJAXRS = isJAXRSResource(resourceClass); + // if (isJAXRS) { + if (resourceClass.isInterface()) { + this.resourceClass = generateResourceClass(resourceClass); + } else { + this.resourceClass = resourceClass; + } + // } else { + // throw new ServiceRuntimeException(resourceClass+" is not a JAX-RS resource class."); + // } + } + + @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(); + 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; + } + } + + public static boolean isJAXRSResource(Class<?> cls) { + for (Annotation a : cls.getAnnotations()) { + if (a.annotationType().getName().startsWith("javax.ws.rs.")) { + return true; + } + } + for (Method method : cls.getMethods()) { + for (Annotation a : method.getAnnotations()) { + if (a.annotationType().getName().startsWith("javax.ws.rs.")) { + return true; + } + } + + /* + for (Annotation[] annotations : method.getParameterAnnotations()) { + for (Annotation a : annotations) { + if (a.annotationType().getName().startsWith("javax.ws.rs.")) { + return true; + } + } + + } + */ + } + return false; + } + + public void stop() { + if (application != null) { + application.destroy(); + } // Unregister the Servlet from the Servlet host servletHost.removeServletMapping(servletMapping); } @@ -180,11 +340,11 @@ public class RESTServiceBindingProvider implements EndpointProvider { public InterfaceContract getBindingInterfaceContract() { return serviceContract; } - + public boolean supportsOneWayInvocation() { return false; } - + /** * Add specific rest interceptor to invocation chain */ @@ -193,12 +353,18 @@ public class RESTServiceBindingProvider implements EndpointProvider { InvocationChain bindingChain = endpoint.getBindingInvocationChain(); if (wfProvider != null) { - bindingChain.addInterceptor(Phase.SERVICE_BINDING_WIREFORMAT, wfProvider.createInterceptor()); + Interceptor interceptor = wfProvider.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); + } } - - if(osProvider != null) { - bindingChain.addInterceptor(Phase.SERVICE_BINDING_OPERATION_SELECTOR, osProvider.createInterceptor()); - } } diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java new file mode 100644 index 0000000000..3d561bf21f --- /dev/null +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/provider/TuscanyRESTServlet.java @@ -0,0 +1,94 @@ +/* + * 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.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.wink.common.internal.registry.ProvidersRegistry; +import org.apache.wink.server.internal.DeploymentConfiguration; +import org.apache.wink.server.internal.RequestProcessor; +import org.apache.wink.server.internal.handlers.ServerMessageContext; +import org.apache.wink.server.internal.servlet.RestServlet; + +/** + * + */ +public class TuscanyRESTServlet extends RestServlet { + private static final long serialVersionUID = 89997233133964915L; + private ExtensionPointRegistry registry; + + public TuscanyRESTServlet(ExtensionPointRegistry registry) { + super(); + this.registry = registry; + } + + @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; + } + + @Override + public RequestProcessor getRequestProcessor() { + return super.getRequestProcessor(); + } + + public ServerMessageContext createMessageContext(HttpServletRequest request, HttpServletResponse response) { + ServerMessageContext messageContext; + try { + messageContext = new ServerMessageContext(request, response, getDeploymentConfiguration()); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + return messageContext; + } + + +} diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java index e73ec961b3..fdcbc92c9c 100644 --- a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatInterceptor.java @@ -54,7 +54,9 @@ public class JSONWireFormatInterceptor implements Interceptor { public Message invoke(Message msg) { HTTPContext bindingContext = (HTTPContext) msg.getBindingContext(); - + if (bindingContext == null) { + return getNext().invoke(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(); diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java index 90efd3390d..c90f5c331c 100644 --- a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/json/provider/JSONWireFormatServiceProvider.java @@ -22,6 +22,7 @@ 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.RESTServiceBindingProvider; 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; @@ -30,6 +31,7 @@ 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; @@ -46,27 +48,44 @@ public class JSONWireFormatServiceProvider implements WireFormatProvider { 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 (RESTServiceBindingProvider.isJAXRSResource(((JavaInterface)interfaze).getJavaClass())) { + return true; + } + } + return false; } public InterfaceContract configureWireFormatInterfaceContract(InterfaceContract interfaceContract) { serviceContract = interfaceContract; - //set JSON databinding - setDataBinding(serviceContract.getInterface()); - - //make JSON databinding default - serviceContract.getInterface().resetDataBinding(JSONDataBinding.NAME); + if (!jaxrs) { + //set JSON databinding + setDataBinding(serviceContract.getInterface()); + + //make JSON databinding default + serviceContract.getInterface().resetDataBinding(JSONDataBinding.NAME); + } return serviceContract; } public Interceptor createInterceptor() { - if(binding.getRequestWireFormat() != null && binding.getRequestWireFormat() instanceof JSONWireFormat) { + if (jaxrs) { + return null; + } + if (binding.getRequestWireFormat() != null && binding.getRequestWireFormat() instanceof JSONWireFormat) { return new JSONWireFormatInterceptor(extensionPoints, endpoint); } return null; diff --git a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java index 508b029b00..9e532d2df2 100644 --- a/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java +++ b/sca-java-2.x/trunk/modules/binding-rest-runtime/src/main/java/org/apache/tuscany/sca/binding/rest/wireformat/xml/provider/XMLWireFormatServiceProvider.java @@ -24,6 +24,7 @@ import java.util.List; import javax.xml.stream.XMLStreamReader; import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.rest.provider.RESTServiceBindingProvider; 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; @@ -32,6 +33,7 @@ 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; @@ -51,26 +53,44 @@ public class XMLWireFormatServiceProvider implements WireFormatProvider { 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 (RESTServiceBindingProvider.isJAXRSResource(((JavaInterface)interfaze).getJavaClass())) { + return true; + } + } + return false; } public InterfaceContract configureWireFormatInterfaceContract(InterfaceContract interfaceContract) { serviceContract = interfaceContract; - - //make XML databinding default - serviceContract.getInterface().resetDataBinding(DATABABINDING); - - //set XML databinding - setDataBinding(serviceContract.getInterface()); + if (!jaxrs) { + + //make XML databinding default + serviceContract.getInterface().resetDataBinding(DATABABINDING); + + //set XML databinding + setDataBinding(serviceContract.getInterface()); + } return serviceContract; } public Interceptor createInterceptor() { - if(binding.getRequestWireFormat() != null && binding.getRequestWireFormat() instanceof XMLWireFormat) { + if (jaxrs) { + return null; + } + if (binding.getRequestWireFormat() != null && binding.getRequestWireFormat() instanceof XMLWireFormat) { return new XMLWireFormatInterceptor(extensionPoints, endpoint); } return null; |