From efccdd821b68280ee3b73c8ef5cda121bc27f620 Mon Sep 17 00:00:00 2001 From: rfeng Date: Sat, 13 Nov 2010 17:42:16 +0000 Subject: Improve jaxb/json databindings and jsonrpc binding to better handle interfaces git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1034820 13f79535-47bb-0310-9956-ffa450edef68 --- .../jsonrpc/provider/JSONRPCBindingInvoker.java | 48 ++-- .../binding/jsonrpc/provider/JSONRPCClient.java | 293 +++++++++++++++++++++ .../jsonrpc/provider/JSONRPCClientInvoker.java | 13 +- .../provider/JSONRPCReferenceBindingProvider.java | 19 +- 4 files changed, 341 insertions(+), 32 deletions(-) create mode 100644 sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClient.java (limited to 'sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org') diff --git a/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingInvoker.java b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingInvoker.java index ecca09dc92..3de6e57dda 100644 --- a/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingInvoker.java +++ b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCBindingInvoker.java @@ -26,9 +26,9 @@ import org.apache.http.client.methods.HttpPost; 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.databinding.json.JSONDataBinding; import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.invocation.DataExchangeSemantics; import org.apache.tuscany.sca.invocation.Invoker; import org.apache.tuscany.sca.invocation.Message; import org.json.JSONArray; @@ -39,18 +39,18 @@ import org.json.JSONObject; * * @version $Rev$ $Date$ */ -public class JSONRPCBindingInvoker implements Invoker { +public class JSONRPCBindingInvoker implements Invoker, DataExchangeSemantics { private EndpointReference endpointReference; private Operation operation; private String uri; - + private HttpClient httpClient; public JSONRPCBindingInvoker(EndpointReference endpointReference, Operation operation, HttpClient httpClient) { this.endpointReference = endpointReference; this.operation = operation; - this.uri = ((JSONRPCBinding) endpointReference.getBinding()).getURI(); - + this.uri = endpointReference.getBinding().getURI(); + this.httpClient = httpClient; } @@ -64,9 +64,9 @@ public class JSONRPCBindingInvoker implements Invoker { final String db = msg.getOperation().getWrapper().getDataBinding(); String req; if (!db.equals(JSONDataBinding.NAME)) { - - JSONObject jsonRequest = null;; + JSONObject jsonRequest = null; + ; Object[] args = null; try { // Extract the method @@ -87,7 +87,7 @@ public class JSONRPCBindingInvoker implements Invoker { } req = jsonRequest.toString(); } else { - req = (String)((Object[])msg.getBody())[0]; + req = (String)((Object[])msg.getBody())[0]; } StringEntity entity = new StringEntity(req, "UTF-8"); post.setEntity(entity); @@ -97,20 +97,20 @@ public class JSONRPCBindingInvoker implements Invoker { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { //success try { - String entityResponse = EntityUtils.toString(response.getEntity()); + String entityResponse = EntityUtils.toString(response.getEntity()); if (!db.equals(JSONDataBinding.NAME)) { JSONObject jsonResponse = new JSONObject(entityResponse); - //check requestId - if (! jsonResponse.getString("id").equalsIgnoreCase(requestId)) { - throw new RuntimeException("Invalid response id:" + requestId ); - } + //check requestId + if (!jsonResponse.getString("id").equalsIgnoreCase(requestId)) { + throw new RuntimeException("Invalid response id:" + requestId); + } - msg.setBody(jsonResponse.get("result")); + msg.setBody(jsonResponse.get("result")); } else { - msg.setBody(entityResponse); + msg.setBody(entityResponse); } - + } catch (Exception e) { //FIXME Exceptions are not handled correctly here // They should be reported to the client JavaScript as proper @@ -125,17 +125,18 @@ public class JSONRPCBindingInvoker implements Invoker { return msg; } - + private static JSONObject getJSONRequest(Message msg) { - - JSONObject jsonRequest = null;; + + JSONObject jsonRequest = null; + ; Object[] args = null; Object id = null; try { // Extract the method jsonRequest = new JSONObject(); jsonRequest.putOpt("method", "Service" + "." + msg.getOperation().getName()); - + // Extract the arguments args = msg.getBody(); JSONArray array = new JSONArray(); @@ -151,5 +152,10 @@ public class JSONRPCBindingInvoker implements Invoker { return jsonRequest; } - + + @Override + public boolean allowsPassByReference() { + return true; + } + } diff --git a/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClient.java b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClient.java new file mode 100644 index 0000000000..f427233e52 --- /dev/null +++ b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClient.java @@ -0,0 +1,293 @@ +/* + * 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.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Iterator; + +import javax.xml.bind.annotation.adapters.XmlAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; + +import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.map.AnnotationIntrospector; +import org.codehaus.jackson.map.DeserializationConfig; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.deser.CustomDeserializerFactory; +import org.codehaus.jackson.map.deser.StdDeserializerProvider; +import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector; +import org.codehaus.jackson.map.ser.CustomSerializerFactory; +import org.codehaus.jackson.map.type.TypeFactory; +import org.codehaus.jackson.xc.JaxbAnnotationIntrospector; +import org.codehaus.jackson.xc.XmlAdapterJsonDeserializer; +import org.codehaus.jackson.xc.XmlAdapterJsonSerializer; +import org.jabsorb.JSONRPCBridge; +import org.jabsorb.JSONRPCResult; +import org.jabsorb.JSONSerializer; +import org.jabsorb.client.ClientError; +import org.jabsorb.client.ErrorResponse; +import org.jabsorb.client.Session; +import org.jabsorb.serializer.FixUp; +import org.jabsorb.serializer.SerializerState; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class JSONRPCClient implements InvocationHandler { + // private static Logger log = LoggerFactory.getLogger(JsonrpcClient.class); + + private Session session; + private JSONSerializer serializer; + + /** + * Maintain a unique id for each message + */ + private int id = 0; + + /** + * Allow access to the serializer + * + * @return The serializer for this class + */ + public JSONSerializer getSerializer() { + return serializer; + } + + /** + * Create a client given a session + * + * @param session -- + * transport session to use for this connection + */ + public JSONRPCClient(Session session) { + try { + this.session = session; + serializer = new JSONSerializer(); + serializer.registerDefaultSerializers(); + serializer.setMarshallClassHints(false); + serializer.setMarshallNullAttributes(false); + } catch (Exception e) { + throw new ClientError(e); + } + } + + private synchronized int getId() { + return id++; + } + + /** Manual instantiation of HashMap */ + private static class ProxyMap extends HashMap { + public String getString(Object key) { + return (String)super.get(key); + } + + public Object putString(String key, Object value) { + return super.put(key, value); + } + } + + private ProxyMap proxyMap = new ProxyMap(); + + /** + * Create a proxy for communicating with the remote service. + * + * @param key + * the remote object key + * @param klass + * the class of the interface the remote object should adhere to + * @return created proxy + */ + public Object openProxy(String key, Class klass) { + Object result = java.lang.reflect.Proxy.newProxyInstance(klass.getClassLoader(), new Class[] {klass}, this); + proxyMap.put(result, key); + return result; + } + + /** + * Dispose of the proxy that is no longer needed + * + * @param proxy + */ + public void closeProxy(Object proxy) { + proxyMap.remove(proxy); + } + + /** + * This method is public because of the inheritance from the + * InvokationHandler -- should never be called directly. + */ + public Object invoke(Object proxyObj, Method method, Object[] args) throws Exception { + String methodName = method.getName(); + if (methodName.equals("hashCode")) { + return new Integer(System.identityHashCode(proxyObj)); + } else if (methodName.equals("equals")) { + return (proxyObj == args[0] ? Boolean.TRUE : Boolean.FALSE); + } else if (methodName.equals("toString")) { + return proxyObj.getClass().getName() + '@' + Integer.toHexString(proxyObj.hashCode()); + } + return invoke(proxyMap.getString(proxyObj), + method.getName(), + args, + method.getReturnType(), + method.getGenericReturnType()); + } + + private Object invoke(String objectTag, String methodName, Object[] args, Class returnType, Type genericReturnType) + throws Exception { + final int id = getId(); + JSONObject message = new JSONObject(); + String methodTag = objectTag == null ? "" : objectTag + "."; + methodTag += methodName; + message.put("method", methodTag); + + { + SerializerState state = new SerializerState(); + + if (args != null) { + + JSONArray params = marshal(args); // (JSONArray)serializer.marshall(state, /* parent */ null, args, "params"); + + if ((state.getFixUps() != null) && (state.getFixUps().size() > 0)) { + JSONArray fixups = new JSONArray(); + for (Iterator i = state.getFixUps().iterator(); i.hasNext();) { + FixUp fixup = (FixUp)i.next(); + fixups.put(fixup.toJSONArray()); + } + message.put("fixups", fixups); + } + message.put("params", params); + } else { + message.put("params", new JSONArray()); + } + } + message.put("id", id); + + JSONObject responseMessage = session.sendAndReceive(message); + + if (!responseMessage.has("result")) { + processException(responseMessage); + } + Object rawResult = responseMessage.get("result"); + if (rawResult == null) { + processException(responseMessage); + } + if (returnType.equals(Void.TYPE)) { + return null; + } + + { + JSONArray fixups = responseMessage.optJSONArray("fixups"); + + if (fixups != null) { + for (int i = 0; i < fixups.length(); i++) { + JSONArray assignment = fixups.getJSONArray(i); + JSONArray fixup = assignment.getJSONArray(0); + JSONArray original = assignment.getJSONArray(1); + JSONRPCBridge.applyFixup(rawResult, fixup, original); + } + } + } + if (returnType.isInterface()) { + ObjectMapper mapper = createObjectMapper(returnType); + return mapper.readValue(rawResult.toString(), TypeFactory.type(genericReturnType)); + } + return serializer.unmarshall(new SerializerState(), returnType, rawResult); + } + + private JSONArray marshal(Object[] args) throws Exception { + if(args==null) { + return new JSONArray(); + } + ObjectMapper mapper = createObjectMapper(null); + String json = mapper.writeValueAsString(args); + return new JSONArray(json); + } + + public static JsonParser createJsonParser(String content) { + JsonFactory jsonFactory = new JsonFactory(); + try { + return jsonFactory.createJsonParser(content); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + public static ObjectMapper createObjectMapper(Class cls) { + ObjectMapper mapper = new ObjectMapper(); + if (cls != null) { + // Workaround for http://jira.codehaus.org/browse/JACKSON-413 + Package pkg = cls.getPackage(); + if (pkg != null) { + XmlJavaTypeAdapters adapters = pkg.getAnnotation(XmlJavaTypeAdapters.class); + if (adapters != null) { + CustomSerializerFactory serializerFactory = new CustomSerializerFactory(); + CustomDeserializerFactory deserializerFactory = new CustomDeserializerFactory(); + for (XmlJavaTypeAdapter a : adapters.value()) { + XmlAdapter xmlAdapter = null; + try { + xmlAdapter = a.value().newInstance(); + } catch (Throwable e) { + // Ignore + } + if (xmlAdapter != null) { + XmlAdapterJsonDeserializer deserializer = new XmlAdapterJsonDeserializer(xmlAdapter); + XmlAdapterJsonSerializer serializer = new XmlAdapterJsonSerializer(xmlAdapter); + deserializerFactory.addSpecificMapping(a.type(), deserializer); + serializerFactory.addGenericMapping(a.type(), serializer); + StdDeserializerProvider deserializerProvider = + new StdDeserializerProvider(deserializerFactory); + mapper.setSerializerFactory(serializerFactory); + mapper.setDeserializerProvider(deserializerProvider); + } + } + } + } + } + AnnotationIntrospector primary = new JaxbAnnotationIntrospector(); + AnnotationIntrospector secondary = new JacksonAnnotationIntrospector(); + AnnotationIntrospector pair = new AnnotationIntrospector.Pair(primary, secondary); + mapper.getDeserializationConfig().setAnnotationIntrospector(pair); + // [rfeng] To avoid complaints about javaClass + mapper.getDeserializationConfig().set(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, Boolean.FALSE); + mapper.getSerializationConfig().setAnnotationIntrospector(pair); + return mapper; + } + + /** + * Generate and throw exception based on the data in the 'responseMessage' + */ + protected void processException(JSONObject responseMessage) throws JSONException { + JSONObject error = (JSONObject)responseMessage.get("error"); + if (error != null) { + Integer code = new Integer(error.has("code") ? error.getInt("code") : 0); + String trace = error.has("trace") ? error.getString("trace") : null; + String msg = error.has("msg") ? error.getString("msg") : null; + throw new ErrorResponse(code, msg, trace); + } else + throw new ErrorResponse(new Integer(JSONRPCResult.CODE_ERR_PARSE), + "Unknown response:" + responseMessage.toString(2), null); + } + +} diff --git a/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClientInvoker.java b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClientInvoker.java index 2b0859c416..3b2d2b707d 100644 --- a/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClientInvoker.java +++ b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCClientInvoker.java @@ -26,9 +26,9 @@ import org.apache.tuscany.sca.assembly.EndpointReference; import org.apache.tuscany.sca.binding.jsonrpc.JSONRPCBinding; 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.jabsorb.client.Client; import org.jabsorb.client.Session; import org.jabsorb.client.TransportRegistry; @@ -37,7 +37,7 @@ import org.jabsorb.client.TransportRegistry; * * @version $Rev$ $Date$ */ -public class JSONRPCClientInvoker implements Invoker { +public class JSONRPCClientInvoker implements Invoker, DataExchangeSemantics { private EndpointReference endpointReference; private Operation operation; private Method method; @@ -52,7 +52,7 @@ public class JSONRPCClientInvoker implements Invoker { public Message invoke(Message msg) { Session session = TransportRegistry.i().createSession(uri); - Client client = new Client(session); + JSONRPCClient client = new JSONRPCClient(session); Object proxy = client.openProxy("", method.getDeclaringClass()); try { @@ -66,5 +66,10 @@ public class JSONRPCClientInvoker implements Invoker { } return msg; } - + + @Override + public boolean allowsPassByReference() { + return true; + } + } diff --git a/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java index adc19202b3..3eaf6ceb7a 100644 --- a/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java +++ b/sca-java-2.x/trunk/modules/binding-jsonrpc-runtime/src/main/java/org/apache/tuscany/sca/binding/jsonrpc/provider/JSONRPCReferenceBindingProvider.java @@ -56,7 +56,7 @@ public class JSONRPCReferenceBindingProvider implements ReferenceBindingProvider public JSONRPCReferenceBindingProvider(EndpointReference endpointReference) { this.endpointReference = endpointReference; - this.reference = (RuntimeComponentReference) endpointReference.getReference(); + this.reference = (RuntimeComponentReference)endpointReference.getReference(); //clone the service contract to avoid databinding issues /* @@ -70,6 +70,10 @@ public class JSONRPCReferenceBindingProvider implements ReferenceBindingProvider */ // Create an HTTP client + httpClient = createHttpClient(); + } + + public HttpClient createHttpClient() { HttpParams defaultParameters = new BasicHttpParams(); //defaultParameters.setIntParameter(HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 10); HttpProtocolParams.setContentCharset(defaultParameters, HTTP.UTF_8); @@ -79,9 +83,10 @@ public class JSONRPCReferenceBindingProvider implements ReferenceBindingProvider SchemeRegistry supportedSchemes = new SchemeRegistry(); supportedSchemes.register(new Scheme(HttpHost.DEFAULT_SCHEME_NAME, PlainSocketFactory.getSocketFactory(), 80)); - ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(defaultParameters, supportedSchemes); + ClientConnectionManager connectionManager = + new ThreadSafeClientConnManager(defaultParameters, supportedSchemes); - httpClient = new DefaultHttpClient(connectionManager, defaultParameters); + return new DefaultHttpClient(connectionManager, defaultParameters); } public InterfaceContract getBindingInterfaceContract() { @@ -90,10 +95,10 @@ public class JSONRPCReferenceBindingProvider implements ReferenceBindingProvider } public Invoker createInvoker(Operation operation) { - final Interface intf = reference.getInterfaceContract().getInterface(); - if (intf.isDynamic()) { - return new JSONRPCBindingInvoker(endpointReference, operation, httpClient); - } + final Interface intf = reference.getInterfaceContract().getInterface(); + if (intf.isDynamic()) { + return new JSONRPCBindingInvoker(endpointReference, operation, httpClient); + } return new JSONRPCClientInvoker(endpointReference, operation, httpClient); } -- cgit v1.2.3