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 */
022 package org.granite.tide.data;
023
024 import java.lang.reflect.Method;
025 import java.lang.reflect.Type;
026 import java.util.concurrent.ConcurrentHashMap;
027 import java.util.concurrent.ConcurrentMap;
028
029 import javassist.util.proxy.MethodFilter;
030 import javassist.util.proxy.MethodHandler;
031 import javassist.util.proxy.ProxyFactory;
032 import javassist.util.proxy.ProxyObject;
033
034 import org.granite.messaging.amf.io.convert.Converter;
035 import org.granite.messaging.amf.io.convert.Converters;
036 import org.granite.util.TypeUtil;
037
038
039 /**
040 * @author William DRAI
041 */
042 public 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 }