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    }