001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.tide.data; 023 024import java.lang.reflect.Method; 025import java.lang.reflect.Type; 026import java.util.concurrent.ConcurrentHashMap; 027import java.util.concurrent.ConcurrentMap; 028 029import javassist.util.proxy.MethodFilter; 030import javassist.util.proxy.MethodHandler; 031import javassist.util.proxy.ProxyFactory; 032import javassist.util.proxy.ProxyObject; 033 034import org.granite.messaging.amf.io.convert.Converter; 035import org.granite.messaging.amf.io.convert.Converters; 036import org.granite.util.TypeUtil; 037 038 039/** 040 * @author William DRAI 041 */ 042public class ChangeSetConverter extends Converter { 043 044 private ConcurrentMap<Class<?>, Class<?>> proxyClasses = new ConcurrentHashMap<Class<?>, Class<?>>(); 045 046 public ChangeSetConverter(Converters converters) { 047 super(converters); 048 } 049 050 @Override 051 protected boolean internalCanConvert(Object value, Type targetType) { 052 if (value instanceof ChangeSet && targetType instanceof Class<?>) { 053 if (((ChangeSet)value).getChanges() == null || ((ChangeSet)value).getChanges().length == 0) 054 throw new IllegalArgumentException("Incoming ChangeSet objects must contain at least one Change"); 055 056 String className = ((ChangeSet)value).getChanges()[0].getClassName(); 057 try { 058 Class<?> clazz = TypeUtil.forName(className); 059 return ((Class<?>)targetType).isAssignableFrom(clazz); 060 } 061 catch (ClassNotFoundException e) { 062 throw new RuntimeException("Cannot find class for ChangeSet argument " + className); 063 } 064 } 065 return false; 066 } 067 068 @Override 069 protected Object internalConvert(Object value, Type targetType) { 070 ChangeSet changeSet = (ChangeSet)value; 071 String className = changeSet.getChanges()[0].getClassName(); 072 try { 073 Class<?> clazz = TypeUtil.forName(className); 074 Class<?> proxyClass = proxyClasses.get(clazz); 075 if (proxyClass == null) { 076 ProxyFactory proxyFactory = new ProxyFactory(); 077 proxyFactory.setFilter(FINALIZE_FILTER); 078 proxyFactory.setSuperclass(clazz); 079 proxyFactory.setInterfaces(new Class<?>[] { ChangeSetProxy.class }); 080 proxyClass = proxyFactory.createClass(); 081 proxyClasses.put(clazz, proxyClass); 082 } 083 084 ProxyObject proxyObject = (ProxyObject)proxyClass.newInstance(); 085 proxyObject.setHandler(new ChangeSetProxyHandler(proxyObject, changeSet)); 086 return proxyObject; 087 } 088 catch (Exception e) { 089 throw new RuntimeException("Cannot build proxy for Change argument " + className); 090 } 091 } 092 093 private static final MethodFilter FINALIZE_FILTER = new MethodFilter() { 094 public boolean isHandled(Method m) { 095 return m.getParameterTypes().length > 0 || !m.getName().equals("finalize"); 096 } 097 }; 098 099 private static class ChangeSetProxyHandler implements MethodHandler { 100 101 private final Object proxyObject; 102 private final ChangeSet changeSet; 103 104 public ChangeSetProxyHandler(ProxyObject proxyObject, ChangeSet changeSet) { 105 this.proxyObject = proxyObject; 106 this.changeSet = changeSet; 107 } 108 109 public Object invoke(Object object, Method method, Method method1, Object[] args) throws Exception { 110 String name = method.getName(); 111 if ("toString".equals(name)) { 112 return changeSet.getChanges()[0].getClassName() + "@" + System.identityHashCode(object); 113 } 114 else if ("equals".equals(name)) { 115 return proxyObject == object; 116 } 117 else if ("hashCode".equals(name)) { 118 return System.identityHashCode(object); 119 } 120 else if ("getChangeSetProxyData".equals(name) && method.getParameterTypes().length == 0) { 121 return changeSet; 122 } 123 return null; 124 } 125 } 126}