/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.capedwarf.jpa; import javassist.util.proxy.MethodFilter; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; /** * Proxy wrapping helper. * * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ abstract class ProxyingHelper implements ProxyingFactory { public <T extends Entity> T createProxy(Class<T> entityClass) throws Exception { if (entityClass == null) throw new IllegalArgumentException("Null entity class"); T instance = entityClass.newInstance(); return wrap(instance); } public boolean isProxy(Entity entity) { if (entity == null) throw new IllegalArgumentException("Null entity"); Class<?> ec = entity.getClass(); return ProxyFactory.isProxyClass(ec); } /** * Get entity manager provider. * * @return the entity manager provider */ protected abstract EntityManagerProvider getProvider(); /** * Get real entity from proxy. * * @param obj the proxy * @return real entity */ @SuppressWarnings({"unchecked"}) protected <T> T getEntity(T obj) { if (obj == null) throw new IllegalArgumentException("Null entity"); if (obj instanceof ProxyEntity == false) return obj; ProxyEntity<T> pe = (ProxyEntity<T>) obj; return pe.getRealEntity(); } /** * Safe wrap real entity into proxy. * e.g. null is allowed as entity parameter * * @param entity the real entity * @return proxy entity or null ir entity is null */ protected <T> T safeWrap(final T entity) { return (entity != null) ? wrap(entity) : null; } /** * Get real class. * * @param clazz the class to check * @return real class */ @SuppressWarnings({"unchecked"}) protected <X> Class<X> getRealClass(Class<X> clazz) { if (ProxyFactory.isProxyClass(clazz)) return (Class<X>) clazz.getSuperclass(); // generic cast should still work else return clazz; } /** * Wrap real entity into proxy. * * @param entity the real entity * @return proxy entity */ @SuppressWarnings({"unchecked"}) protected <T> T wrap(final T entity) { if (entity == null) throw new IllegalArgumentException("Null entity"); Class<?> ec = entity.getClass(); // do not wrap if disabled or existing proxies or disabled proxy entities or not an persistent entity if (ProxyingUtils.isDisabled() || ProxyFactory.isProxyClass(ec) || ec.isAnnotationPresent(DisableProxy.class) || ec.isAnnotationPresent(javax.persistence.Entity.class) == false) return entity; ProxyFactory factory = new ProxyFactory(); factory.setFilter(FINALIZE_FILTER); factory.setSuperclass(ec); // expose our entity's class factory.setInterfaces(new Class[]{ProxyEntity.class}); // expose ProxyEntity Class<?> proxyClass = getProxyClass(factory); ProxyObject proxy; try { proxy = (ProxyObject) proxyClass.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } proxy.setHandler(new MethodHandler() { public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable { // Is this ProxyEntity::getRealEntity() invocation if ((args == null || args.length == 0) && "getRealEntity".equals(method.getName())) { return entity; } // hash code if ((args == null || args.length == 0) && "hashCode".equals(method.getName())) { return entity.hashCode(); } // equals if ((args != null && args.length == 1) && "equals".equals(method.getName())) { Object other = args[0]; return other != null && entity.equals(getEntity(other)); } ManyToOne mto = method.getAnnotation(ManyToOne.class); if (mto != null) { return Relationships.handleManyToOne(entity, method, args, mto, getProvider()); } OneToMany otm = method.getAnnotation(OneToMany.class); if (otm != null) { return Relationships.handleOneToMany(entity, method, otm, ProxyingHelper.this); } return method.invoke(entity, args); } }); return (T) proxy; } protected static Class<?> getProxyClass(ProxyFactory factory) { SecurityManager sm = System.getSecurityManager(); if (sm == null) return factory.createClass(); else return AccessController.doPrivileged(new ClassCreator(factory)); } /** * Privileged class creator. */ protected static class ClassCreator implements PrivilegedAction<Class<?>> { private ProxyFactory factory; public ClassCreator(ProxyFactory factory) { this.factory = factory; } public Class<?> run() { return factory.createClass(); } } private static final MethodFilter FINALIZE_FILTER = new MethodFilter() { public boolean isHandled(Method m) { // skip finalize methods return !("finalize".equals(m.getName()) && m.getParameterTypes().length == 0); } }; }