diff options
Diffstat (limited to 'sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca')
6 files changed, 535 insertions, 0 deletions
diff --git a/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCBinding.java b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCBinding.java new file mode 100644 index 0000000000..44c799f286 --- /dev/null +++ b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCBinding.java @@ -0,0 +1,30 @@ +/* + * 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; + + +/** + * A model for the JSONRPC binding. + */ +public class JSONRPCBinding { + + // empty as right now the json-rpc binding has no attributes + +} diff --git a/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCBindingActivator.java b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCBindingActivator.java new file mode 100644 index 0000000000..84f09c8561 --- /dev/null +++ b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCBindingActivator.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.jsonrpc; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.extension.helper.BindingActivator; +import org.apache.tuscany.sca.extension.helper.ComponentLifecycle; +import org.apache.tuscany.sca.extension.helper.InvokerFactory; +import org.apache.tuscany.sca.host.http.ServletHost; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentReference; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.osoa.sca.ServiceRuntimeException; + +public class JSONRPCBindingActivator implements BindingActivator<JSONRPCBinding> { + + protected ServletHost servletHost; + + public JSONRPCBindingActivator(ServletHost servletHost) { + this.servletHost = servletHost; + } + + public Class<JSONRPCBinding> getBindingClass() { + return JSONRPCBinding.class; + } + + public InvokerFactory createInvokerFactory(RuntimeComponent rc, RuntimeComponentReference rcr, Binding b, JSONRPCBinding binding) { + throw new ServiceRuntimeException("SCA reference support not yet implemented"); + } + + public ComponentLifecycle createService(RuntimeComponent rc, RuntimeComponentService rcs, Binding b, JSONRPCBinding binding) { + return new JSONRPCService(rc, rcs, b, binding, servletHost); + } + +} diff --git a/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCService.java b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCService.java new file mode 100644 index 0000000000..94bc6a5d6b --- /dev/null +++ b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCService.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.jsonrpc; + +import java.net.URI; + +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.core.invocation.JDKProxyFactory; +import org.apache.tuscany.sca.core.invocation.ProxyFactory; +import org.apache.tuscany.sca.extension.helper.ComponentLifecycle; +import org.apache.tuscany.sca.host.http.ServletHost; +import org.apache.tuscany.sca.interfacedef.Interface; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; + +/** + * Implementation of the JSONRPC binding provider. + * + * There are multiple servlets used to support the JSON-RPC binidng. + * One servlet to handle requests for the scaDomain script and seperate + * servlets for each SCA <service> which uses <binding.jsonrpc>. + */ +public class JSONRPCService implements ComponentLifecycle { + + private RuntimeComponentService service; + private ServletHost servletHost; + private Binding binding; + + public static final String SERVICE_PREFIX = "/SCADomain/"; + + // path to the scaDomain.js script + // Note: this is the same as the Ajax binding to keep the client code + // the same for clients using either the ajax or jsonrpc binding + public static final String SCA_DOMAIN_SCRIPT = SERVICE_PREFIX + "scaDomain.js"; + + + public JSONRPCService(RuntimeComponent component, + RuntimeComponentService service, + Binding b, + JSONRPCBinding binding, + ServletHost servletHost) { + this.service = service; + this.binding = b; + this.servletHost = servletHost; + } + + public void start() { + + // Determine the service business interface + Class<?> serviceInterface = getTargetJavaClass(service.getInterfaceContract().getInterface()); + + // Create a Java proxy to the target service + ProxyFactory proxyFactory = new JDKProxyFactory(); + Object proxy = proxyFactory.createProxy(serviceInterface, service.getRuntimeWire(binding)); + + // Create and register a servlet for this service + JSONRPCServiceServlet serviceServlet = new JSONRPCServiceServlet(binding.getName(), serviceInterface, proxy); + int port; + servletHost.addServletMapping(binding.getURI(), serviceServlet); + URI uri = URI.create(binding.getURI()); + port = uri.getPort(); + if (port == -1) + port = 8080; + + // get the ScaDomainScriptServlet, if it doesn't yet exist create one + // this uses removeServletMapping / addServletMapping as theres no getServletMapping facility + URI domainURI = URI.create("http://localhost:" + port + SCA_DOMAIN_SCRIPT); + ScaDomainScriptServlet scaDomainServlet = (ScaDomainScriptServlet) servletHost.removeServletMapping(domainURI.toString()); + if (scaDomainServlet == null) { + scaDomainServlet = new ScaDomainScriptServlet(); + } + servletHost.addServletMapping(domainURI.toString(), scaDomainServlet); + + // Add this service to the scadomain script servlet + scaDomainServlet.addService(binding.getName()); + + } + + public void stop() { + + // Unregister from the service servlet mapping + int port; + servletHost.removeServletMapping(binding.getURI()); + URI uri = URI.create(binding.getURI()); + port = uri.getPort(); + if (port == -1) + port = 8080; + + // Unregister the service from the scaDomain script servlet + // don't unregister the scaDomain script servlet if it still has other service names + URI domainURI = URI.create("http://localhost:" + port + SCA_DOMAIN_SCRIPT); + ScaDomainScriptServlet scaDomainServlet = (ScaDomainScriptServlet) servletHost.removeServletMapping(domainURI.toString()); + if (scaDomainServlet != null) { + scaDomainServlet.removeService(binding.getName()); + // put it back if there are still other services registered with the servlet + if (scaDomainServlet.getServiceNames().size() > 0) { + servletHost.addServletMapping(domainURI.toString(), scaDomainServlet); + } + } + } + + protected 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-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCServiceServlet.java b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCServiceServlet.java new file mode 100644 index 0000000000..b9eb71e259 --- /dev/null +++ b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JSONRPCServiceServlet.java @@ -0,0 +1,160 @@ +/* + * 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; + +import java.io.BufferedReader; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.text.ParseException; + +import javax.servlet.ServletConfig; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.json.JSONObject; + +import com.metaparadigm.jsonrpc.JSONRPCBridge; +import com.metaparadigm.jsonrpc.JSONRPCResult; +import com.metaparadigm.jsonrpc.JSONRPCServlet; + +/** + * Servlet that handles JSON-RPC requests invoking SCA services. + * + * There is an instance of this servlet for each <binding.jsonrpc> + */ +public class JSONRPCServiceServlet extends JSONRPCServlet { + private static final long serialVersionUID = 1L; + + transient String serviceName; + transient Object serviceInstance; + transient Class<?> serviceInterface; + + public JSONRPCServiceServlet(String serviceName, Class<?> serviceInterface, Object serviceInstance) { + this.serviceName = serviceName; + this.serviceInterface = serviceInterface; + this.serviceInstance = serviceInstance; + } + + /** + * Override to do nothing as the JSONRPCServlet is setup by the + * service method in this class. + */ + @Override + public void init(ServletConfig config) { + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws IOException { + + if (request.getParameter("smd") != null) { + handleSMDRequest(request, response); + } else { + try { + handleServiceRequest(request, response); + } finally { + HttpSession session = request.getSession(); + if (session != null) { + session.removeAttribute("JSONRPCBridge"); + } + } + } + } + + private void handleServiceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { + /* + * Create a new bridge for every request to aviod all the problems with + * JSON-RPC-Java storing the bridge in the session + */ + HttpSession session = request.getSession(); + + JSONRPCBridge jsonrpcBridge = new JSONRPCBridge(); + jsonrpcBridge.registerObject(serviceName, serviceInstance, serviceInterface); + session.setAttribute("JSONRPCBridge", jsonrpcBridge); + + // Encode using UTF-8, although We are actually ASCII clean as + // all unicode data is JSON escaped using backslash u. This is + // less data efficient for foreign character sets but it is + // needed to support naughty browsers such as Konqueror and Safari + // which do not honour the charset set in the response + response.setContentType("text/plain;charset=utf-8"); + OutputStream out = response.getOutputStream(); + + // 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"; + } + BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), charset)); + + // Read the request + CharArrayWriter data = new CharArrayWriter(); + char buf[] = new char[4096]; + int ret; + while ((ret = in.read(buf, 0, 4096)) != -1) { + data.write(buf, 0, ret); + } + + JSONObject jsonReq = null; + JSONRPCResult jsonResp = null; + try { + jsonReq = new JSONObject(data.toString()); + } catch (ParseException e) { + throw new RuntimeException("Unable to parse request", e); + } + + String method = jsonReq.getString("method"); + if ((method != null) && (method.indexOf('.') < 0)) { + jsonReq.putOpt("method", serviceName + "." + method); + } + + // invoke the request + jsonResp = jsonrpcBridge.call(new Object[] {request}, jsonReq); + + byte[] bout = jsonResp.toString().getBytes("UTF-8"); + + out.write(bout); + out.flush(); + out.close(); + } + + /** + * 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 = JavaToSmd.interfaceToSmd(serviceInterface, serviceUrl); + + response.setContentType("text/plain;charset=utf-8"); + OutputStream out = response.getOutputStream(); + byte[] bout = smd.getBytes("UTF-8"); + + out.write(bout); + out.flush(); + out.close(); + } + +} diff --git a/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JavaToSmd.java b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JavaToSmd.java new file mode 100644 index 0000000000..196d973a76 --- /dev/null +++ b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/JavaToSmd.java @@ -0,0 +1,54 @@ +/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ package org.apache.tuscany.sca.binding.jsonrpc;
+
+import java.lang.reflect.Method;
+
+/**
+ * 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
+ */
+public class JavaToSmd {
+
+ public static String interfaceToSmd(Class klazz, String serviceUrl) {
+ String name = klazz.getSimpleName();
+ Method methods[] = klazz.getMethods();
+
+ StringBuffer smdSb = new StringBuffer();
+ smdSb.append("{\"SMDVersion\":\".1\",\"objectName\":\"" + name + "\",\"serviceType\":\"JSON-RPC\",\"serviceURL\":\""+ serviceUrl + "\",\"methods\":[");
+ for (int i = 0; i < methods.length; i++) {
+ if (i != 0) smdSb.append(",");
+ Class<?> params[] = methods[i].getParameterTypes();
+ smdSb.append("{\"name\":\""+methods[i].getName() + "\",\"parameters\":[");
+ for (int j = 0; j < params.length; j++) {
+ if (j != 0) smdSb.append(",");
+ // right now Dojo doesn't look at the type value, so we'll default it to STRING
+ // also, since we can't introspect the method paramater names we'll just create an incrementing parameter name
+ smdSb.append("{\"name\":\"param" + j + "\",\"type\":\"STRING\"}");
+ }
+ smdSb.append("]}");
+ }
+ smdSb.append("]}");
+
+ return smdSb.toString();
+ }
+
+}
diff --git a/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/ScaDomainScriptServlet.java b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/ScaDomainScriptServlet.java new file mode 100644 index 0000000000..7930a9b34c --- /dev/null +++ b/sca-java-1.x/tags/0.99-incubating/modules/binding-jsonrpc/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/ScaDomainScriptServlet.java @@ -0,0 +1,112 @@ +/* + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Servlet to handle requests for the scaDomain.js script. + * + * This script wrappers the JSON-RPC-Java jsonrpc.js script + * adding in at the bottom the JavaScript to create and initialize + * the variables representing the SCA services. + */ +public class ScaDomainScriptServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + protected transient List<String> serviceNames; + + public ScaDomainScriptServlet() { + serviceNames = new ArrayList<String>(); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + + ServletOutputStream os = response.getOutputStream(); + + os.println(); + os.println("/* Apache Tuscany scaDomain.js Header */"); + os.println(); + + writeJSONRPCJavaScript(os); + writeScaDomainCode(os, request.getServletPath()); + } + + /** + * Creates a JavaScript variable within the scaDomain script for each SCA service and + * initializes its value with the JSON-RPC client to invoke the serverside service. + */ + protected void writeScaDomainCode(ServletOutputStream out, String path) throws IOException { + out.println(); + out.println("/* Apache Tuscany scaDomain.js Footer */"); + out.println(); + out.println("function scaDomain() {}"); + + // remove the leading slash '/' character + path = path.substring(1); + + for (String serviceName : serviceNames) { + out.println(); + + // A slight hack to make the service function available with a variable named 'serviceName' + // to do that the JSONRpcClient is added to the scaDomain and then the service function is got from that + out.println("scaDomain." + serviceName + " = " + "new JSONRpcClient(\"" + path + "/" + serviceName + "\");"); + out.println(serviceName + " = scaDomain." + serviceName + "." + serviceName + ";"); + } + + out.println(); + out.println("/** End of Apache Tuscany scaDomain.js */"); + out.println(); + } + + /** + * Reads the jsonrpc.js script from the classpath and adds its contents to the output stream. + */ + protected void writeJSONRPCJavaScript(ServletOutputStream os) throws IOException { + URL url = getClass().getResource("jsonrpc.js"); + InputStream is = url.openStream(); + int i; + while ((i = is.read()) != -1) { + os.write(i); + } + } + + public void addService(String serviceName) { + serviceNames.add(serviceName); + } + + public void removeService(String serviceName) { + serviceNames.remove(serviceName); + } + + public List<String> getServiceNames() { + return serviceNames; + } + +} |