Java tutorial
/* * JSON-RPC-Java - a JSON-RPC to Java Bridge with dynamic invocation * * $Id: BeanSerializer.java,v 1.7.2.2 2006/03/06 12:39:21 mclark Exp $ * * Copyright Metaparadigm Pte. Ltd. 2004. * Michael Clark <michael@metaparadigm.com> * * Licensed 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 com.metaparadigm.jsonrpc; import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.logging.Logger; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.beans.Introspector; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.beans.BeanInfo; import org.json.JSONException; import org.json.JSONObject; public class BeanSerializer extends AbstractSerializer { private final static long serialVersionUID = 1; private final static Logger log = Logger.getLogger(BeanSerializer.class.getName()); private static HashMap beanCache = new HashMap(); private static Class[] _serializableClasses = new Class[] {}; private static Class[] _JSONClasses = new Class[] {}; public Class[] getSerializableClasses() { return _serializableClasses; } public Class[] getJSONClasses() { return _JSONClasses; } @Override public boolean canSerialize(Class clazz, Class jsonClazz) { return (!clazz.isArray() && !clazz.isPrimitive() && !clazz.isInterface() && (jsonClazz == null || jsonClazz == JSONObject.class)); } protected static class BeanData { // in absence of getters and setters, these fields are // public to allow subclasses to access. public BeanInfo beanInfo; public HashMap readableProps; public HashMap writableProps; } protected static class BeanSerializerState { // in absence of getters and setters, these fields are // public to allow subclasses to access. // Circular reference detection public HashSet beanSet = new HashSet(); } public static BeanData analyzeBean(Class clazz) throws IntrospectionException { // log.info("analyzing " + clazz.getName()); BeanData bd = new BeanData(); bd.beanInfo = Introspector.getBeanInfo(clazz, Object.class); PropertyDescriptor props[] = bd.beanInfo.getPropertyDescriptors(); bd.readableProps = new HashMap(); bd.writableProps = new HashMap(); for (int i = 0; i < props.length; i++) { if (props[i].getWriteMethod() != null) { bd.writableProps.put(props[i].getName(), props[i].getWriteMethod()); } if (props[i].getReadMethod() != null) { bd.readableProps.put(props[i].getName(), props[i].getReadMethod()); } } return bd; } public static BeanData getBeanData(Class clazz) throws IntrospectionException { BeanData bd; synchronized (beanCache) { bd = (BeanData) beanCache.get(clazz); if (bd == null) { bd = analyzeBean(clazz); beanCache.put(clazz, bd); } } return bd; } public ObjectMatch tryUnmarshall(SerializerState state, Class clazz, Object o) throws UnmarshallException { JSONObject jso = (JSONObject) o; BeanData bd = null; try { bd = getBeanData(clazz); } catch (IntrospectionException e) { throw new UnmarshallException(clazz.getName() + " is not a bean"); } int match = 0, mismatch = 0; Iterator i = bd.writableProps.entrySet().iterator(); while (i.hasNext()) { Map.Entry ent = (Map.Entry) i.next(); String prop = (String) ent.getKey(); Method method = (Method) ent.getValue(); if (jso.has(prop)) match++; else mismatch++; } if (match == 0) throw new UnmarshallException("bean has no matches"); ObjectMatch m = null, tmp = null; i = jso.keys(); while (i.hasNext()) { String field = (String) i.next(); Method setMethod = (Method) bd.writableProps.get(field); if (setMethod != null) { try { Class param[] = setMethod.getParameterTypes(); if (param.length != 1) throw new UnmarshallException("bean " + clazz.getName() + " method " + setMethod.getName() + " does not have one arg"); tmp = ser.tryUnmarshall(state, param[0], jso.get(field)); if (m == null) m = tmp; else m = m.max(tmp); } catch (UnmarshallException e) { throw new UnmarshallException("bean " + clazz.getName() + " " + e.getMessage()); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { mismatch++; } } return m.max(new ObjectMatch(mismatch)); } public Object unmarshall(SerializerState state, Class clazz, Object o) throws UnmarshallException { JSONObject jso = (JSONObject) o; BeanData bd = null; try { bd = getBeanData(clazz); } catch (IntrospectionException e) { throw new UnmarshallException(clazz.getName() + " is not a bean"); } if (ser.isDebug()) log.fine("instantiating " + clazz.getName()); Object instance = null; try { instance = clazz.newInstance(); } catch (Exception e) { throw new UnmarshallException("can't instantiate bean " + clazz.getName() + ": " + e.getMessage()); } Object invokeArgs[] = new Object[1]; Object fieldVal = null; Iterator i = jso.keys(); while (i.hasNext()) { String field = (String) i.next(); Method setMethod = (Method) bd.writableProps.get(field); if (setMethod != null) { try { Class param[] = setMethod.getParameterTypes(); fieldVal = ser.unmarshall(state, param[0], jso.get(field)); } catch (UnmarshallException e) { throw new UnmarshallException("bean " + clazz.getName() + " " + e.getMessage()); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (ser.isDebug()) log.fine("invoking " + setMethod.getName() + "(" + fieldVal + ")"); invokeArgs[0] = fieldVal; try { setMethod.invoke(instance, invokeArgs); } catch (Throwable e) { if (e instanceof InvocationTargetException) e = ((InvocationTargetException) e).getTargetException(); throw new UnmarshallException("bean " + clazz.getName() + "can't invoke " + setMethod.getName() + ": " + e.getMessage()); } } } return instance; } public Object marshall(SerializerState state, Object o) throws MarshallException { BeanSerializerState beanState; try { beanState = (BeanSerializerState) state.get(BeanSerializerState.class); } catch (Exception e) { throw new MarshallException("bean serializer internal error"); } Integer identity = new Integer(System.identityHashCode(o)); if (beanState.beanSet.contains(identity)) throw new MarshallException("circular reference"); beanState.beanSet.add(identity); BeanData bd = null; try { bd = getBeanData(o.getClass()); } catch (IntrospectionException e) { throw new MarshallException(o.getClass().getName() + " is not a bean"); } JSONObject val = new JSONObject(); if (ser.getMarshallClassHints()) try { val.put("javaClass", o.getClass().getName()); } catch (JSONException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } Iterator i = bd.readableProps.entrySet().iterator(); Object args[] = new Object[0]; Object result = null; while (i.hasNext()) { Map.Entry ent = (Map.Entry) i.next(); String prop = (String) ent.getKey(); Method getMethod = (Method) ent.getValue(); if (ser.isDebug()) log.fine("invoking " + getMethod.getName() + "()"); try { result = getMethod.invoke(o, args); } catch (Throwable e) { if (e instanceof InvocationTargetException) e = ((InvocationTargetException) e).getTargetException(); throw new MarshallException("bean " + o.getClass().getName() + " can't invoke " + getMethod.getName() + ": " + e.getMessage()); } try { if (result != null || ser.getMarshallNullAttributes()) val.put(prop, ser.marshall(state, result)); } catch (MarshallException e) { throw new MarshallException("bean " + o.getClass().getName() + " " + e.getMessage()); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } beanState.beanSet.remove(identity); return val; } }