diff options
Diffstat (limited to 'sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany')
17 files changed, 1826 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/js/dojo/JSONRPCBindingJavascriptProxyFactory.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/js/dojo/JSONRPCBindingJavascriptProxyFactory.java new file mode 100644 index 0000000000..59a804a561 --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/js/dojo/JSONRPCBindingJavascriptProxyFactory.java @@ -0,0 +1,77 @@ +/* + * 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.js.dojo; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +import javax.xml.namespace.QName; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.assembly.ComponentReference; +import org.apache.tuscany.sca.assembly.Endpoint; +import org.apache.tuscany.sca.assembly.EndpointReference; +import org.apache.tuscany.sca.binding.jsonrpc.JSONRPCBinding; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; +import org.apache.tuscany.sca.web.javascript.JavascriptProxyFactory; + +public class JSONRPCBindingJavascriptProxyFactory implements JavascriptProxyFactory { + private static final QName NAME = JSONRPCBinding.TYPE; + + public Class<?> getModelType() { + return JSONRPCBinding.class; + } + + public QName getQName() { + return NAME; + } + + public String getJavascriptProxyFile() { + return null; + } + + public InputStream getJavascriptProxyFileAsStream() throws IOException { + return null; + } + + public String createJavascriptHeader(ComponentReference componentReference) throws IOException { + return "dojo.require('dojo.rpc.JsonService');"; + } + + public String createJavascriptReference(ComponentReference componentReference) throws IOException { + EndpointReference epr = componentReference.getEndpointReferences().get(0); + Endpoint targetEndpoint = epr.getTargetEndpoint(); + if (targetEndpoint.isUnresolved()) { + //force resolution and targetEndpoint binding calculations + //by calling the getInvocationChain + ((RuntimeEndpointReference) epr).getInvocationChains(); + targetEndpoint = epr.getTargetEndpoint(); + } + + Binding binding = targetEndpoint.getBinding(); + + URI targetURI = URI.create(binding.getURI()); + String targetPath = targetURI.getPath(); + + return "dojo.rpc.JsonService(\"" + targetPath + "?smd\")"; + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc10Request.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc10Request.java new file mode 100644 index 0000000000..547d36abaf --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc10Request.java @@ -0,0 +1,70 @@ +/* + * 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.protocol; + +import java.io.OutputStream; + +import org.apache.tuscany.sca.databinding.json.jackson.JacksonHelper; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.ArrayNode; +import org.codehaus.jackson.node.JsonNodeFactory; +import org.codehaus.jackson.node.ObjectNode; + +/** + * http://json-rpc.org/wiki/specification + * A remote method is invoked by sending a request to a remote service. The request is a single object serialized using JSON. + * <br>It has three properties: + * <ul> + * <li>method - A String containing the name of the method to be invoked. + * <li>params - An Array of objects to pass as arguments to the method. + * <li>id - The request id. This can be of any type. It is used to match the response with the request that it is replying to. + * </ul> + */ +public class JsonRpc10Request extends JsonRpcRequest { + + public JsonRpc10Request(String id, String method, Object[] params) { + super(JsonNodeFactory.instance.textNode(id), method, params); + } + + public JsonRpc10Request(ObjectNode req) { + super(req); + method = req.get("method").getTextValue(); + id = req.get("id"); + JsonNode args = req.get("params"); + if (args instanceof ArrayNode) { + // Positional parameters + ArrayNode array = (ArrayNode)args; + params = new Object[array.size()]; + for (int i = 0; i < params.length; i++) { + params[i] = array.get(i); + } + } else if (args == null) { + params = new Object[0]; + } else { + throw new IllegalArgumentException("Invalid request: params is not a JSON array - " + args); + } + } + + public void write(OutputStream os) throws Exception { + JacksonHelper.MAPPER.writeValue(os, getJsonNode()); + + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc10Response.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc10Response.java new file mode 100644 index 0000000000..4ca941580a --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc10Response.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.jsonrpc.protocol; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.ObjectNode; + +/** + * When the method invocation completes, the service must reply with a response. The response is a single object serialized using JSON. + * <br> + * It has three properties: + * + * <ul> + * <li>result - The Object that was returned by the invoked method. This must be null in case there was an error invoking the method. + * <li>error - An Error object if there was an error invoking the method. It must be null if there was no error. + * <li>id - This must be the same id as the request it is responding to. + * </ul> + */ +public class JsonRpc10Response extends JsonRpcResponse { + + public JsonRpc10Response(JsonNode id, JsonNode result) { + super(id, result); + } + + public JsonRpc10Response(JsonNode id, Throwable t) { + super(id, t); + } + + public JsonRpc10Response(ObjectNode response) { + super(response); + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20BatchRequest.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20BatchRequest.java new file mode 100644 index 0000000000..44627ddb08 --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20BatchRequest.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.protocol; + +import java.util.ArrayList; +import java.util.List; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.ArrayNode; +import org.codehaus.jackson.node.ObjectNode; + +public class JsonRpc20BatchRequest { + private List<JsonRpc20Request> requests = new ArrayList<JsonRpc20Request>(); + // The corresponding batch response + private JsonRpc20BatchResponse batchResponse; + + public JsonRpc20BatchRequest(ArrayNode array) { + super(); + batchResponse = new JsonRpc20BatchResponse(); + for (int i = 0; i < array.size(); i++) { + JsonNode req = array.get(i); + if (req instanceof ObjectNode) { + try { + requests.add(new JsonRpc20Request((ObjectNode)req)); + batchResponse.getResponses().add(null); + } catch (Exception e) { + // We should return invalid request errors + JsonRpc20Error error = + new JsonRpc20Error(null, JsonRpc20Error.PARSE_ERROR, JsonRpc20Error.PARSE_ERROR_MSG, req); + batchResponse.getResponses().add(error); + } + } else { + // We should return invalid request errors + JsonRpc20Error error = + new JsonRpc20Error(null, JsonRpc20Error.INVALID_REQUEST, JsonRpc20Error.INVALID_REQUEST_MSG, req); + batchResponse.getResponses().add(error); + } + } + } + + public List<JsonRpc20Request> getRequests() { + return requests; + } + + public JsonRpc20BatchResponse getBatchResponse() { + return batchResponse; + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20BatchResponse.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20BatchResponse.java new file mode 100644 index 0000000000..d571458192 --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20BatchResponse.java @@ -0,0 +1,47 @@ +/* + * 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.protocol; + +import java.util.ArrayList; +import java.util.List; + +import org.codehaus.jackson.node.ArrayNode; +import org.codehaus.jackson.node.JsonNodeFactory; + +public class JsonRpc20BatchResponse { + private List<JsonRpcResponse> results = new ArrayList<JsonRpcResponse>(); + + public JsonRpc20BatchResponse() { + super(); + } + + public List<JsonRpcResponse> getResponses() { + return results; + } + + public ArrayNode toJSONArray() { + ArrayNode jsonArray = JsonNodeFactory.instance.arrayNode(); + for (JsonRpcResponse result : results) { + jsonArray.add(result.getJsonNode()); + } + return jsonArray; + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Error.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Error.java new file mode 100644 index 0000000000..30c61515ee --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Error.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.protocol; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.JsonNodeFactory; +import org.codehaus.jackson.node.ObjectNode; + +public class JsonRpc20Error extends JsonRpcResponse { + + public static final int PARSE_ERROR = 32700; + public static final String PARSE_ERROR_MSG = + "Parse error: Invalid JSON was received by the server. " + "An error occurred on the server while parsing the JSON text."; + + public static final int INVALID_REQUEST = -32600; + public static final String INVALID_REQUEST_MSG = "Invalid Request: The JSON sent is not a valid Request object."; + + public static final int METHOD_NOT_FOUND = -32601; + public static final String METHOD_NOT_FOUND_MSG = "Method not found: The method does not exist / is not available."; + + public static final int INVALID_PARAMS = -32602; + public static final String INVALID_PARAMS_MSG = "Invalid params Invalid method parameter(s)."; + + public static final int INTERNAL_ERROR = -32603; + public static final String INTERNAL_ERROR_MSG = "Internal error Internal JSON-RPC error."; + + // -32099 to -32000 Server error Reserved for implementation-defined server-errors. + + public JsonRpc20Error(JsonNode id, Throwable t) { + super(id, t); + this.jsonNode.put("jsonrpc", "2.0"); + this.jsonNode.put("code", INTERNAL_ERROR); + } + + public JsonRpc20Error(JsonNode id, int code, String message, JsonNode data) { + super(id, JsonNodeFactory.instance.nullNode()); + this.jsonNode.put("jsonrpc", "2.0"); + ObjectNode error = JsonNodeFactory.instance.objectNode(); + error.put("code", code); + error.put("message", message); + error.put("data", data); + this.jsonNode.put("error", error); + } + + public JsonRpc20Error(ObjectNode jsonNode) { + super(jsonNode); + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Request.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Request.java new file mode 100644 index 0000000000..22b3b7057f --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Request.java @@ -0,0 +1,113 @@ +/* + * 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.protocol; + +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.tuscany.sca.databinding.json.jackson.JacksonHelper; +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.json.JSONException; + +public class JsonRpc20Request extends JsonRpcRequest { + + protected ObjectNode mappedParams; + + public JsonRpc20Request(String id, String method, Object[] params) { + super(JsonNodeFactory.instance.textNode(id), method, params); + this.jsonNode.put("jsonrpc", "2.0"); + this.mappedParams = null; + } + + public JsonRpc20Request(String id, String method, ObjectNode mappedParams) { + super(JsonNodeFactory.instance.textNode(id), method, null); + this.jsonNode.put("jsonrpc", "2.0"); + this.mappedParams = mappedParams; + this.jsonNode.put("params", mappedParams); + } + + public JsonRpc20Request(ObjectNode req) throws JSONException { + super(req); + JsonNode node = req.get("jsonrpc"); + boolean v20 = node != null && "2.0".equals(node.getTextValue()); + if (!v20) { + throw new IllegalArgumentException("Invalid request: jsonrpc attribute must be \"2.0\""); + } + method = req.get("method").getTextValue(); + JsonNode idNode = req.get("id"); + if (idNode != null) { + id = idNode; + } + JsonNode args = req.get("params"); + if (args instanceof ArrayNode) { + // Positional parameters + ArrayNode array = (ArrayNode)args; + params = new Object[array.size()]; + for (int i = 0; i < params.length; i++) { + params[i] = array.get(i); + } + } else if (args instanceof ObjectNode) { + ObjectNode map = (ObjectNode)args; + mappedParams = map; + params = null; + } else if (args == null) { + params = new Object[0]; + } else { + throw new IllegalArgumentException("Invalid request: params is not a JSON array - " + args); + } + } + + public void write(OutputStream os) throws Exception { + // Construct a map to hold JSON-RPC request + final Map<String, Object> jsonRequest = new HashMap<String, Object>(); + jsonRequest.put("jsonrpc", "2.0"); + jsonRequest.put("id", id); + jsonRequest.put("method", method); + + if (mappedParams != null) { + jsonRequest.put("params", mappedParams); + } + + else { + List<Object> parameters = null; + + if (params != null) { + parameters = Arrays.asList(params); + } else { + parameters = Collections.emptyList(); + } + + jsonRequest.put("params", parameters); + } + JacksonHelper.MAPPER.writeValue(os, jsonRequest); + + } + + public ObjectNode getMappedParams() { + return mappedParams; + } +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Response.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Response.java new file mode 100644 index 0000000000..aa433ede6c --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpc20Response.java @@ -0,0 +1,37 @@ +/* + * 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.protocol; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.ObjectNode; + +public class JsonRpc20Response extends JsonRpcResponse { + + public JsonRpc20Response(JsonNode id, JsonNode result) { + super(id, result); + this.jsonNode.put("jsonrpc", "2.0"); + + } + + public JsonRpc20Response(ObjectNode jsonNode) { + super(jsonNode); + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpcRequest.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpcRequest.java new file mode 100644 index 0000000000..cd98a30976 --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpcRequest.java @@ -0,0 +1,92 @@ +/* + * 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.protocol; + +import java.io.OutputStream; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.ArrayNode; +import org.codehaus.jackson.node.JsonNodeFactory; +import org.codehaus.jackson.node.NullNode; +import org.codehaus.jackson.node.ObjectNode; + +/** + * http://json-rpc.org/wiki/specification + * A remote method is invoked by sending a request to a remote service. The request is a single object serialized using JSON. + * <br>It has three properties: + * <ul> + * <li>method - A String containing the name of the method to be invoked. + * <li>params - An Array of objects to pass as arguments to the method. + * <li>id - The request id. This can be of any type. It is used to match the response with the request that it is replying to. + * </ul> + */ +public abstract class JsonRpcRequest { + protected String method; + protected JsonNode id; + protected Object[] params; + + protected ObjectNode jsonNode; + + public JsonRpcRequest(ObjectNode jsonNode) { + super(); + this.jsonNode = jsonNode; + } + + public JsonRpcRequest(JsonNode id, String method, Object[] params) { + super(); + this.id = id; + this.method = method; + this.params = params; + ObjectNode req = JsonNodeFactory.instance.objectNode(); + req.put("method", method); + req.put("id", id); + if (params != null) { + ArrayNode args = JsonNodeFactory.instance.arrayNode(); + for (Object p : params) { + args.add(JsonNodeFactory.instance.POJONode(p)); + } + req.put("params", args); + } + this.jsonNode = req; + } + + public abstract void write(OutputStream os) throws Exception; + + public boolean isNotification() { + return id == null || (id instanceof NullNode); + } + + public String getMethod() { + return method; + } + + public JsonNode getId() { + return id; + } + + public Object[] getParams() { + return params; + } + + public ObjectNode getJsonNode() { + return jsonNode; + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpcResponse.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpcResponse.java new file mode 100644 index 0000000000..8186d89030 --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/protocol/JsonRpcResponse.java @@ -0,0 +1,98 @@ +/* + * 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.protocol; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +import org.apache.tuscany.sca.databinding.json.jackson.JacksonHelper; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.node.JsonNodeFactory; +import org.codehaus.jackson.node.ObjectNode; + +/** + * When the method invocation completes, the service must reply with a response. The response is a single object serialized using JSON. + * <br> + * It has three properties: + * + * <ul> + * <li>result - The Object that was returned by the invoked method. This must be null in case there was an error invoking the method. + * <li>error - An Error object if there was an error invoking the method. It must be null if there was no error. + * <li>id - This must be the same id as the request it is responding to. + * </ul> + */ +public abstract class JsonRpcResponse { + public static final int REMOTE_EXCEPTION = 490; + + protected ObjectNode jsonNode; + + public JsonRpcResponse(ObjectNode response) { + super(); + this.jsonNode = response; + } + + public JsonRpcResponse(JsonNode id, Throwable t) { + super(); + ObjectNode response = JsonNodeFactory.instance.objectNode(); + response.put("id", id); + // response.put("result", JsonNodeFactory.instance.nullNode()); + response.put("error", mapError(t)); + this.jsonNode = response; + } + + public JsonRpcResponse(JsonNode id, JsonNode result) { + super(); + ObjectNode response = JsonNodeFactory.instance.objectNode(); + response.put("id", id); + response.put("result", result); + // response.put("error", JsonNodeFactory.instance.nullNode()); + this.jsonNode = response; + } + + private ObjectNode mapError(Throwable t) { + ObjectNode obj = JsonNodeFactory.instance.objectNode(); + // obj.put("msg", t.getMessage()); + obj.put("code", REMOTE_EXCEPTION); + obj.put("message", t.getMessage()); + ObjectNode exception = JsonNodeFactory.instance.objectNode(); + exception.put("class", t.getClass().getName()); + exception.put("message", t.getMessage()); + exception.put("stackTrace", JsonRpc20Error.stackTrace(t)); + obj.put("data", exception); + return obj; + } + + public void write(Writer writer) throws IOException { + JacksonHelper.MAPPER.writeValue(writer, jsonNode); + } + + public static String stackTrace(Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + + public ObjectNode getJsonNode() { + return jsonNode; + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-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-Beta4-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-Beta4-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<JSONRPCBinding> { + + 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<JSONRPCBinding> getModelType() { + return JSONRPCBinding.class; + } + +} diff --git a/sca-java-2.x/tags/2.0-Beta4-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-Beta4-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-Beta4-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<Operation> operations = interfaze.getOperations(); + for (Operation operation : operations) { + operation.setDataBinding(JSONDataBinding.NAME); + DataType<List<DataType>> inputType = operation.getInputType(); + if (inputType != null) { + List<DataType> 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-Beta4-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-Beta4-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-Beta4-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-Beta4-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-Beta4-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-Beta4-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<String> servletMappings = new ArrayList<String>(); + + 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-Beta4-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-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JavaToSmd.java new file mode 100644 index 0000000000..3cbcdf735d --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-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.json.JSONArray; +import org.json.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-Beta4-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-Beta4-RC1/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JsonRpcInvoker.java new file mode 100644 index 0000000000..713122f479 --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-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.getBinding().getURI(); + 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<List<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<? extends Throwable> exceptionClass = + (Class<? extends Throwable>)Class.forName(javaClass, false, classLoader); + Constructor<? extends Throwable> 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-Beta4-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-Beta4-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-Beta4-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<Operation> 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(); + } + +} |