From 132aa8a77685ec92bc90c03f987650d275a7b639 Mon Sep 17 00:00:00 2001 From: lresende Date: Mon, 30 Sep 2013 06:59:11 +0000 Subject: 2.0.1 RC1 release tag git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1527464 13f79535-47bb-0310-9956-ffa450edef68 --- .../provider/JSONRPCBindingProviderFactory.java | 66 ++++ .../jsonrpc/provider/JSONRPCDatabindingHelper.java | 67 ++++ .../provider/JSONRPCReferenceBindingProvider.java | 91 ++++++ .../provider/JSONRPCServiceBindingProvider.java | 137 ++++++++ .../sca/binding/jsonrpc/provider/JavaToSmd.java | 131 ++++++++ .../binding/jsonrpc/provider/JsonRpcInvoker.java | 254 ++++++++++++++ .../binding/jsonrpc/provider/JsonRpcServlet.java | 364 +++++++++++++++++++++ 7 files changed, 1110 insertions(+) create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingProviderFactory.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCDatabindingHelper.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCServiceBindingProvider.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JavaToSmd.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcInvoker.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcServlet.java (limited to 'sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider') diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingProviderFactory.java b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingProviderFactory.java new file mode 100644 index 0000000000..8ecadf4a92 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingProviderFactory.java @@ -0,0 +1,66 @@ +/* + * 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.jsonrpc.provider; + +import org.apache.tuscany.sca.binding.jsonrpc.JSONRPCBinding; +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.host.http.client.HttpClientFactory; +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; + +/** + * JSON-RPC Provider Factory + * + * @version $Rev$ $Date$ + */ +public class JSONRPCBindingProviderFactory implements BindingProviderFactory { + + private MessageFactory messageFactory; + private ServletHost servletHost; + private HttpClientFactory httpClientFactory; + + public JSONRPCBindingProviderFactory(ExtensionPointRegistry extensionPoints) { + this.servletHost = ServletHostHelper.getServletHost(extensionPoints); + FactoryExtensionPoint modelFactories = extensionPoints.getExtensionPoint(FactoryExtensionPoint.class); + messageFactory = modelFactories.getFactory(MessageFactory.class); + this.httpClientFactory = HttpClientFactory.getInstance(extensionPoints); + } + + public ReferenceBindingProvider createReferenceBindingProvider(RuntimeEndpointReference endpointReference) { + + return new JSONRPCReferenceBindingProvider(httpClientFactory, endpointReference); + } + + public ServiceBindingProvider createServiceBindingProvider(RuntimeEndpoint endpoint) { + return new JSONRPCServiceBindingProvider(endpoint, messageFactory, servletHost); + } + + public Class getModelType() { + return JSONRPCBinding.class; + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCDatabindingHelper.java b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCDatabindingHelper.java new file mode 100644 index 0000000000..059e44cc7e --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCDatabindingHelper.java @@ -0,0 +1,67 @@ +/* + * 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.jsonrpc.provider; + +import java.util.List; + +import org.apache.tuscany.sca.databinding.json.JSONDataBinding; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.Interface; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.codehaus.jackson.JsonNode; + +/** + * JSONRPC Binding helper class to handle setting the proper + * data binding in the interface contract for JSONRPC + * services and references + * + * @version $Rev$ $Date$ + */ +public class JSONRPCDatabindingHelper { + + @SuppressWarnings("unchecked") + static void setDataBinding(Interface interfaze) { + List operations = interfaze.getOperations(); + for (Operation operation : operations) { + operation.setDataBinding(JSONDataBinding.NAME); + DataType> inputType = operation.getInputType(); + if (inputType != null) { + List logical = inputType.getLogical(); + for (DataType inArg : logical) { + inArg.setDataBinding(JSONDataBinding.NAME); + inArg.setPhysical(JsonNode.class); +// if (!SimpleJavaDataBinding.NAME.equals(inArg.getDataBinding()) || inArg.getPhysical() == BigDecimal.class) { +// inArg.setDataBinding(JSONDataBinding.NAME); +// } + } + } + + for (DataType outputType : operation.getOutputType().getLogical()) { + if (outputType != null) { + outputType.setDataBinding(JSONDataBinding.NAME); + outputType.setPhysical(JsonNode.class); +// if (!SimpleJavaDataBinding.NAME.equals(outputType.getDataBinding()) || outputType.getPhysical() == BigDecimal.class) { +// outputType.setDataBinding(JSONDataBinding.NAME); +// } + } + } + } + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java new file mode 100644 index 0000000000..38688a8d34 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java @@ -0,0 +1,91 @@ +/* + * 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.jsonrpc.provider; + +import org.apache.http.client.HttpClient; +import org.apache.tuscany.sca.assembly.EndpointReference; +import org.apache.tuscany.sca.host.http.client.HttpClientFactory; +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.ReferenceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponentReference; + +/** + * Implementation of the JSONRPC Binding Provider for References + * + * @version $Rev$ $Date$ + */ +public class JSONRPCReferenceBindingProvider implements ReferenceBindingProvider { + + private EndpointReference endpointReference; + private RuntimeComponentReference reference; + private InterfaceContract referenceContract; + + private HttpClientFactory httpClientFactory; + private HttpClient httpClient; + + public JSONRPCReferenceBindingProvider(HttpClientFactory httpClientFactory, EndpointReference endpointReference) { + this.httpClientFactory = httpClientFactory; + this.endpointReference = endpointReference; + this.reference = (RuntimeComponentReference)endpointReference.getReference(); + + + //clone the service contract to avoid databinding issues + /* + try { + this.referenceContract = (InterfaceContract)reference.getInterfaceContract().clone(); + } catch(CloneNotSupportedException e) { + this.referenceContract = reference.getInterfaceContract(); + } + + JSONRPCDatabindingHelper.setDataBinding(referenceContract.getInterface()); + */ + + // Create an HTTP client + // httpClient = createHttpClient(); + } + + public InterfaceContract getBindingInterfaceContract() { + //return referenceContract; + return reference.getInterfaceContract(); + } + + public Invoker createInvoker(Operation operation) { + // final Interface intf = reference.getInterfaceContract().getInterface(); + return new JsonRpcInvoker(endpointReference, operation, httpClient); + } + + public void start() { + // Create an HTTP client + httpClient = httpClientFactory.createHttpClient(); + } + + public void stop() { + if (httpClient != null) { + httpClient.getConnectionManager().shutdown(); + } + } + + public boolean supportsOneWayInvocation() { + return false; + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCServiceBindingProvider.java b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCServiceBindingProvider.java new file mode 100644 index 0000000000..82b4c351a3 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCServiceBindingProvider.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.jsonrpc.provider; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.Servlet; + +import org.apache.tuscany.sca.binding.jsonrpc.JSONRPCBinding; +import org.apache.tuscany.sca.host.http.ServletHost; +import org.apache.tuscany.sca.interfacedef.Interface; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.invocation.MessageFactory; +import org.apache.tuscany.sca.provider.ServiceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + + +/** + * Implementation of the JSONRPC Binding Provider for Services + * + * @version $Rev$ $Date$ + */ +public class JSONRPCServiceBindingProvider implements ServiceBindingProvider { + private MessageFactory messageFactory; + + private RuntimeEndpoint endpoint; + private RuntimeComponent component; + private RuntimeComponentService service; + private InterfaceContract serviceContract; + private JSONRPCBinding binding; + private ServletHost servletHost; + private List servletMappings = new ArrayList(); + + public JSONRPCServiceBindingProvider(RuntimeEndpoint endpoint, + MessageFactory messageFactory, + ServletHost servletHost) { + this.endpoint = endpoint; + this.component = (RuntimeComponent)endpoint.getComponent(); + this.service = (RuntimeComponentService)endpoint.getService(); + this.binding = (JSONRPCBinding) endpoint.getBinding(); + this.messageFactory = messageFactory; + this.servletHost = servletHost; + + //clone the service contract to avoid databinding issues + try { + this.serviceContract = (InterfaceContract)service.getInterfaceContract().clone(); + } catch(CloneNotSupportedException e) { + this.serviceContract = service.getInterfaceContract(); + } + + JSONRPCDatabindingHelper.setDataBinding(serviceContract.getInterface()); + } + + public InterfaceContract getBindingInterfaceContract() { + return serviceContract; + } + + public boolean supportsOneWayInvocation() { + return false; + } + + public void start() { + // Set default databinding to json + // replaced by JSONRPCDatabindingHelper.setDataBinding(serviceContract.getInterface()); above + //serviceContract.getInterface().resetDataBinding(JSONDataBinding.NAME); + + // Determine the service business interface + Class serviceInterface = getTargetJavaClass(serviceContract.getInterface()); + + // Create a Java proxy to the target service + Object proxy = component.getComponentContext().getServiceReference(serviceInterface, endpoint).getService(); + + // Create and register a Servlet for this service + JsonRpcServlet serviceServlet = + new JsonRpcServlet(messageFactory, endpoint, serviceInterface, proxy); + String mapping = registerServlet(serviceServlet); + servletMappings.add(mapping); + } + + public String registerServlet(Servlet servlet) { + // Create our HTTP service listener Servlet and register it with the + // Servlet host + String servletMapping = binding.getURI(); + if (!servletMapping.endsWith("/")) { + servletMapping += "/"; + } + if (!servletMapping.endsWith("*")) { + servletMapping += "*"; + } + + String mappedURI = servletHost.addServletMapping(servletMapping, servlet); + String deployedURI = mappedURI; + if (deployedURI.endsWith("*")) { + deployedURI = deployedURI.substring(0, deployedURI.length() - 1); + } + binding.setURI(deployedURI); + endpoint.setDeployedURI(deployedURI); + return mappedURI; + } + + public void stop() { + + // Remove the Servlet mappings we've added + for (String mapping: servletMappings) { + servletHost.removeServletMapping(mapping); + } + + } + + private Class getTargetJavaClass(Interface targetInterface) { + // TODO: right now assume that the target is always a Java + // Implementation. Need to figure out how to generate Java + // Interface in cases where the target is not a Java Implementation + return ((JavaInterface)targetInterface).getJavaClass(); + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JavaToSmd.java b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JavaToSmd.java new file mode 100644 index 0000000000..12fe6836c6 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JavaToSmd.java @@ -0,0 +1,131 @@ +/* + * 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.jsonrpc.provider; + +import java.lang.reflect.Method; +import java.util.Collection; + +import org.apache.wink.json4j.JSONArray; +import org.apache.wink.json4j.JSONObject; + +/** + * Utility class to create a Simple Method Description (SMD) descriptor + * from a Java class. See http://dojo.jot.com/SMD. + * + * TODO: Change to work from the Tuscany Interface instead of a Java class + * + * @version $Rev$ $Date$ + */ +public class JavaToSmd { + + public static String interfaceToSmd(Class klazz, String serviceUrl) { + try { + String name = klazz.getSimpleName(); + Method[] methods = klazz.getMethods(); + + JSONObject smd = new JSONObject(); + smd.put("SMDVersion", ".1"); + smd.put("objectName", name); + smd.put("serviceType", "JSON-RPC"); + smd.put("serviceURL", serviceUrl); + + JSONArray services = new JSONArray(); + for (int i = 0; i < methods.length; i++) { + JSONObject service = new JSONObject(); + Class[] params = methods[i].getParameterTypes(); + JSONArray paramArray = new JSONArray(); + for (int j = 0; j < params.length; j++) { + JSONObject param = new JSONObject(); + param.put("name", "param" + j); + param.put("type", getJSONType(params[j])); + paramArray.put(param); + } + service.put("name", methods[i].getName()); + service.put("parameters", paramArray); + services.put(service); + } + + smd.put("methods", services); + + return smd.toString(2); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + + } + + public static String interfaceToSmd20(Class klazz, String serviceUrl) { + try { + String name = klazz.getSimpleName(); + Method[] methods = klazz.getMethods(); + + JSONObject smd = new JSONObject(); + smd.put("SMDVersion", "2.0"); + smd.put("transport", "POST"); + smd.put("envelope", "JSON-RPC-1.0"); + smd.put("target", serviceUrl); + smd.put("id", klazz.getName()); + smd.put("description", "JSON-RPC service provided by Tuscany: " + name); + + JSONObject services = new JSONObject(); + for (int i = 0; i < methods.length; i++) { + JSONObject service = new JSONObject(); + Class[] params = methods[i].getParameterTypes(); + JSONArray paramArray = new JSONArray(); + for (int j = 0; j < params.length; j++) { + JSONObject param = new JSONObject(); + param.put("name", "param" + j); + param.put("type", getJSONType(params[j])); + paramArray.put(param); + } + service.put("parameters", paramArray); + services.put(methods[i].getName(), service); + } + + smd.put("services", services); + + return smd.toString(2); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + + } + + private static String getJSONType(Class type) { + if (type == boolean.class || type == Boolean.class) { + return "boolean"; + } + if (type == String.class) { + return "string"; + } + if (byte.class == type || short.class == type + || int.class == type + || long.class == type + || float.class == type + || double.class == type + || Number.class.isAssignableFrom(type)) { + return "number"; + } + if (type.isArray() || Collection.class.isAssignableFrom(type)) { + return "array"; + } + return "object"; + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcInvoker.java b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcInvoker.java new file mode 100644 index 0000000000..ca21599b14 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcInvoker.java @@ -0,0 +1,254 @@ +/* + * 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.jsonrpc.provider; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.List; +import java.util.UUID; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentProducer; +import org.apache.http.entity.EntityTemplate; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import org.apache.tuscany.sca.assembly.EndpointReference; +import org.apache.tuscany.sca.binding.jsonrpc.JSONRPCBinding; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc10Request; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc20Request; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpcRequest; +import org.apache.tuscany.sca.databinding.json.JSONDataBinding; +import org.apache.tuscany.sca.databinding.json.jackson.JacksonHelper; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.invocation.DataExchangeSemantics; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.type.TypeFactory; +import org.codehaus.jackson.node.NullNode; +import org.codehaus.jackson.node.ObjectNode; +import org.oasisopen.sca.ServiceRuntimeException; + +/** + * Invoker for the JSONRPC Binding + * + * @version $Rev$ $Date$ + */ +public class JsonRpcInvoker implements Invoker, DataExchangeSemantics { + private EndpointReference endpointReference; + private Operation operation; + private String uri; + private HttpClient httpClient; + + public JsonRpcInvoker(EndpointReference endpointReference, Operation operation, HttpClient httpClient) { + this.endpointReference = endpointReference; + this.operation = operation; + this.uri = endpointReference.getDeployedURI(); + this.httpClient = httpClient; + } + + public Message invoke(Message msg) { + HttpPost post = null; + HttpResponse response = null; + try { + String requestId = UUID.randomUUID().toString(); + post = new HttpPost(uri); + HttpEntity entity = null; + Object[] args = msg.getBody(); + final String db = msg.getOperation().getInputWrapper().getDataBinding(); + + if (!db.equals(JSONDataBinding.NAME)) { + Object[] params = new Object[0]; + // Extract the arguments + args = msg.getBody(); + + if (args instanceof Object[]) { + params = (Object[])args; + } + + JsonRpcRequest req = null; + if (JSONRPCBinding.VERSION_20.equals(((JSONRPCBinding)endpointReference.getBinding()).getVersion())) { + req = new JsonRpc20Request(requestId, msg.getOperation().getName(), params); + } else { + req = new JsonRpc10Request(requestId, msg.getOperation().getName(), params); + } + final JsonRpcRequest json = req; + + // Create content producer so that we can stream the json result out + ContentProducer cp = new ContentProducer() { + public void writeTo(OutputStream outstream) throws IOException { + // mapper.writeValue(outstream, req.toJSONObject().toString()); + try { + json.write(outstream); + } catch (Exception e) { + throw new IOException(e); + } + } + }; + entity = new EntityTemplate(cp); + } else { + // Single string argument + entity = new StringEntity((String)args[0], "UTF-8"); + } + + post.setEntity(entity); + + response = httpClient.execute(post); + + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + //success + + entity = response.getEntity(); + String entityResponse = EntityUtils.toString(entity); + entity.consumeContent(); + if (!db.equals(JSONDataBinding.NAME)) { + ObjectNode jsonResponse = (ObjectNode)JacksonHelper.MAPPER.readTree(entityResponse); + + if (jsonResponse.has("error") && jsonResponse.get("error") != NullNode.instance) { + processException(jsonResponse); + } + DataType> outputType = operation.getOutputType(); + DataType returnType = + (outputType != null && !outputType.getLogical().isEmpty()) ? outputType.getLogical().get(0) + : null; + + if (returnType == null) { + msg.setBody(null); + return msg; + } + + //check requestId + if (!requestId.equalsIgnoreCase(jsonResponse.get("id").getTextValue())) { + throw new ServiceRuntimeException("Invalid response id:" + requestId); + } + + JsonNode rawResult = jsonResponse.get("result"); + + Class returnClass = returnType.getPhysical(); + Type genericReturnType = returnType.getGenericType(); + + ObjectMapper mapper = createObjectMapper(returnClass); + String json = mapper.writeValueAsString(rawResult); + + Object body = mapper.readValue(json, TypeFactory.type(genericReturnType)); + + msg.setBody(body); + } else { + msg.setBody(entityResponse); + } + + } else { + // Consume the content so the connection can be released + response.getEntity().consumeContent(); + throw new ServiceRuntimeException("Abnormal HTTP response: " + response.getStatusLine().toString()); + } + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + // e.printStackTrace(); + msg.setFaultBody(e); + } catch (Throwable e) { + throw new ServiceRuntimeException(e); + } + + return msg; + } + + public static ObjectMapper createObjectMapper(Class cls) { + return JacksonHelper.createObjectMapper(cls); + } + + private String opt(ObjectNode node, String name) { + JsonNode value = node.get(name); + if (value == null) { + return null; + } else { + return value.getValueAsText(); + } + } + + /** + * Generate and throw exception based on the data in the 'responseMessage' + */ + protected void processException(ObjectNode responseMessage) throws Throwable { + // FIXME: We need to find a way to build Java exceptions out of the json-rpc error + JsonNode error = responseMessage.get("error"); + if (error != null) { + Object data = error.get("data"); + if (data instanceof ObjectNode) { + ObjectNode fault = (ObjectNode)data; + String javaClass = opt(fault, "class"); + String message = opt(fault, "message"); + String stackTrace = opt(fault, "stackTrace"); + if (javaClass != null) { + if (stackTrace != null) { + message = message + "\n" + stackTrace; + } + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (operation instanceof JavaOperation) { + Method method = ((JavaOperation)operation).getJavaMethod(); + classLoader = method.getDeclaringClass().getClassLoader(); + } + Class exceptionClass = + (Class)Class.forName(javaClass, false, classLoader); + Constructor ctor = null; + Throwable ex = null; + try { + ctor = exceptionClass.getConstructor(String.class, Throwable.class); + ex = ctor.newInstance(message, null); + } catch (NoSuchMethodException e1) { + try { + ctor = exceptionClass.getConstructor(String.class); + ex = ctor.newInstance(message); + } catch (NoSuchMethodException e2) { + try { + ctor = exceptionClass.getConstructor(Throwable.class); + ex = ctor.newInstance(null); + } catch (NoSuchMethodException e3) { + ctor = exceptionClass.getConstructor(); + ex = ctor.newInstance(); + } + } + } + throw ex; + } + } + throw new ServiceRuntimeException(error.toString()); + } + } + + @Override + public boolean allowsPassByReference() { + return true; + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcServlet.java b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcServlet.java new file mode 100644 index 0000000000..4261f5bffc --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcServlet.java @@ -0,0 +1,364 @@ +/* + * 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.jsonrpc.provider; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.List; + +import javax.security.auth.login.LoginException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.codec.binary.Base64; +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.binding.jsonrpc.JSONRPCBinding; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc10Request; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc10Response; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc20BatchRequest; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc20Error; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc20Request; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpc20Response; +import org.apache.tuscany.sca.binding.jsonrpc.protocol.JsonRpcResponse; +import org.apache.tuscany.sca.databinding.json.JSONDataBinding; +import org.apache.tuscany.sca.databinding.json.jackson.JacksonHelper; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.invocation.MessageFactory; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.ArrayNode; +import org.codehaus.jackson.node.JsonNodeFactory; +import org.codehaus.jackson.node.ObjectNode; +import org.oasisopen.sca.ServiceRuntimeException; + +public class JsonRpcServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + transient MessageFactory messageFactory; + + transient Binding binding; + transient String serviceName; + transient Object serviceInstance; + transient RuntimeEndpoint endpoint; + transient Class serviceInterface; + + public JsonRpcServlet(MessageFactory messageFactory, + RuntimeEndpoint endpoint, + Class serviceInterface, + Object serviceInstance) { + this.endpoint = endpoint; + this.messageFactory = messageFactory; + this.binding = endpoint.getBinding(); + this.serviceName = binding.getName(); + this.serviceInterface = serviceInterface; + this.serviceInstance = serviceInstance; + } + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, + IOException { + + if ("smd".equals(request.getQueryString())) { + handleSMDRequest(request, response); + return; + } + try { + handleJsonRpcInvocation(request, response); + + } catch (RuntimeException re) { + if (re.getCause() instanceof javax.security.auth.login.LoginException) { + response.setHeader("WWW-Authenticate", "BASIC realm=\"" + "ldap-realm" + "\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } else { + throw re; + } + } + + } + + private void handleJsonRpcInvocation(HttpServletRequest request, HttpServletResponse response) + throws UnsupportedEncodingException, IOException, ServletException { + + // Decode using the charset in the request if it exists otherwise + // use UTF-8 as this is what all browser implementations use. + // The JSON-RPC-Java JavaScript client is ASCII clean so it + // although here we can correctly handle data from other clients + // that do not escape non ASCII data + String charset = request.getCharacterEncoding(); + if (charset == null) { + charset = "UTF-8"; + } + + JsonNode root = null; + if (request.getMethod().equals("GET")) { + // if using GET Support (see http://groups.google.com/group/json-rpc/web/json-rpc-over-http) + + //parse the GET QueryString + try { + String params = + new String(Base64.decodeBase64(URLDecoder.decode(request.getParameter("params"), charset) + .getBytes())); + StringBuilder sb = new StringBuilder(); + sb.append("{"); + sb.append("\"method\": \"" + request.getParameter("method") + "\","); + sb.append("\"params\": " + params + ","); + sb.append("\"id\":" + request.getParameter("id")); + sb.append("}"); + + root = JacksonHelper.MAPPER.readTree(sb.toString()); + } catch (Throwable e) { + JsonRpc10Response error = + new JsonRpc10Response(JsonNodeFactory.instance.textNode(request.getParameter("id")), e); + error.write(response.getWriter()); + return; + } + } else { + root = JacksonHelper.MAPPER.readTree(request.getReader()); + } + + try { + if (root.isArray()) { + ArrayNode input = (ArrayNode)root; + JsonRpc20BatchRequest batchReq = new JsonRpc20BatchRequest(input); + for (int i = 0; i < batchReq.getRequests().size(); i++) { + JsonRpcResponse result = batchReq.getBatchResponse().getResponses().get(i); + if (result == null) { + result = invoke(batchReq.getRequests().get(i)); + batchReq.getBatchResponse().getResponses().set(i, result); + } + } + ArrayNode responses = batchReq.getBatchResponse().toJSONArray(); + JacksonHelper.MAPPER.writeValue(response.getWriter(), responses); + } else { + if (root.has("jsonrpc")) { + JsonRpc20Request jsonReq = new JsonRpc20Request((ObjectNode)root); + JsonRpcResponse jsonResult = invoke(jsonReq); + if (jsonResult != null) { + jsonResult.write(response.getWriter()); + } + } else { + JsonRpc10Request jsonReq = new JsonRpc10Request((ObjectNode)root); + JsonRpc10Response jsonResult = invoke(jsonReq); + if (jsonResult != null) { + jsonResult.write(response.getWriter()); + } + } + } + } catch (Throwable e) { + throw new ServletException(e); + } + } + + private JsonRpcResponse invoke(JsonRpc20Request request) throws Exception { + if (request.isNotification()) { + return null; + } + // invoke the request + String method = request.getMethod(); + Object[] params = request.getParams(); + + Object result = null; + Operation jsonOperation = findOperation(method); + + // Invoke the get operation on the service implementation + Message requestMessage = messageFactory.createMessage(); + requestMessage.setOperation(jsonOperation); + + requestMessage.getHeaders().put("RequestMessage", request); + + if (jsonOperation.getInputWrapper().getDataBinding().equals(JSONDataBinding.NAME)) { + requestMessage.setBody(new Object[] {JacksonHelper.toString(request.getJsonNode())}); + } else { + requestMessage.setBody(params); + } + requestMessage.setBody(params); + + Message responseMessage = null; + try { + + //result = wire.invoke(jsonOperation, args); + responseMessage = endpoint.getInvocationChain(jsonOperation).getHeadInvoker().invoke(requestMessage); + } catch (RuntimeException re) { + if (re.getCause() instanceof javax.security.auth.login.LoginException) { + throw re; + } else { + JsonRpc20Error error = new JsonRpc20Error(request.getId(), re); + return error; + } + } + + if (!responseMessage.isFault()) { + if (jsonOperation.getOutputWrapper().getDataBinding().equals(JSONDataBinding.NAME)) { + result = responseMessage.getBody(); + return new JsonRpc20Response((ObjectNode)JacksonHelper.MAPPER.readTree(result.toString())); + } else { + if (jsonOperation.getOutputType().getLogical().size() == 0) { + // void operation (json-rpc notification) + try { + JsonRpc20Response response = new JsonRpc20Response(request.getId(), null); + return response; + } catch (Exception e) { + throw new ServiceRuntimeException("Unable to create JSON response", e); + } + + } else { + // regular operation returning some value + try { + result = responseMessage.getBody(); + JsonRpc20Response response = new JsonRpc20Response(request.getId(), (JsonNode)result); + //get response to send to client + return response; + } catch (Exception e) { + throw new ServiceRuntimeException("Unable to create JSON response", e); + } + } + } + + } else { + //exception thrown while executing the invocation + Throwable exception = (Throwable)responseMessage.getBody(); + + JsonRpc20Error error = new JsonRpc20Error(request.getId(), exception); + return error; + } + + } + + private JsonRpc10Response invoke(JsonRpc10Request request) throws Exception { + if (request.isNotification()) { + return null; + } + // invoke the request + String method = request.getMethod(); + Object[] params = request.getParams(); + + Object result = null; + Operation jsonOperation = findOperation(method); + + // Invoke the get operation on the service implementation + Message requestMessage = messageFactory.createMessage(); + requestMessage.setOperation(jsonOperation); + + requestMessage.getHeaders().put("RequestMessage", request); + if (jsonOperation.getInputWrapper().getDataBinding().equals(JSONDataBinding.NAME)) { + requestMessage.setBody(new Object[] {JacksonHelper.toString(request.getJsonNode())}); + } else { + requestMessage.setBody(params); + } + + Message responseMessage = null; + try { + + //result = wire.invoke(jsonOperation, args); + responseMessage = endpoint.getInvocationChain(jsonOperation).getHeadInvoker().invoke(requestMessage); + } catch (RuntimeException e) { + if (e.getCause() instanceof LoginException) { + throw e; + } else { + JsonRpc10Response error = new JsonRpc10Response(request.getId(), e); + return error; + } + } + + if (!responseMessage.isFault()) { + if (jsonOperation.getOutputWrapper().getDataBinding().equals(JSONDataBinding.NAME)) { + result = responseMessage.getBody(); + return new JsonRpc10Response((ObjectNode)JacksonHelper.MAPPER.readTree(result.toString())); + } else { + if (jsonOperation.getOutputType().getLogical().size() == 0) { + // void operation (json-rpc notification) + JsonRpc10Response response = + new JsonRpc10Response(request.getId(), JsonNodeFactory.instance.nullNode()); + return response; + + } else { + // regular operation returning some value + result = responseMessage.getBody(); + JsonRpc10Response response = new JsonRpc10Response(request.getId(), (JsonNode)result); + //get response to send to client + return response; + } + } + + } else { + //exception thrown while executing the invocation + Throwable exception = (Throwable)responseMessage.getBody(); + + JsonRpc10Response error = new JsonRpc10Response(request.getId(), exception); + return error; + } + + } + + /** + * Find the operation from the component service contract + * @param componentService + * @param method + * @return + */ + private Operation findOperation(String method) { + if (method.contains(".")) { + method = method.substring(method.lastIndexOf(".") + 1); + } + + List operations = endpoint.getComponentServiceInterfaceContract().getInterface().getOperations(); + //endpoint.getComponentTypeServiceInterfaceContract().getInterface().getOperations(); + //componentService.getBindingProvider(binding).getBindingInterfaceContract().getInterface().getOperations(); + + Operation result = null; + for (Operation o : operations) { + if (o.isDynamic()) + return o; + if (o.getName().equalsIgnoreCase(method)) { + result = o; + break; + } + } + + return result; + } + + /** + * handles requests for the SMD descriptor for a service + */ + protected void handleSMDRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, + UnsupportedEncodingException { + String serviceUrl = request.getRequestURL().toString(); + String smd = null; + if (JSONRPCBinding.VERSION_20.equals(((JSONRPCBinding)binding).getVersion())) { + smd = JavaToSmd.interfaceToSmd20(serviceInterface, serviceUrl); + } else { + smd = JavaToSmd.interfaceToSmd(serviceInterface, serviceUrl); + } + + response.setContentType("application/json;charset=utf-8"); + OutputStream out = response.getOutputStream(); + byte[] bout = smd.getBytes("UTF-8"); + out.write(bout); + out.flush(); + out.close(); + } + +} -- cgit v1.2.3