/* * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved. * * This file is part of the Jspresso framework. * * Jspresso 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 3 of the License, or * (at your option) any later version. * * Jspresso 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 Jspresso. If not, see <http://www.gnu.org/licenses/>. */ package org.jspresso.framework.model.entity.basic; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.jspresso.framework.model.component.ComponentException; import org.jspresso.framework.model.component.IComponent; import org.jspresso.framework.model.component.IComponentCollectionFactory; import org.jspresso.framework.model.component.IComponentExtensionFactory; import org.jspresso.framework.model.component.IComponentFactory; import org.jspresso.framework.model.component.IComponentPropertyStore; import org.jspresso.framework.model.component.basic.AbstractComponentInvocationHandler; import org.jspresso.framework.model.component.basic.BasicComponentPropertyStore; import org.jspresso.framework.model.descriptor.IComponentDescriptor; import org.jspresso.framework.model.descriptor.IStringPropertyDescriptor; import org.jspresso.framework.model.entity.IEntity; import org.jspresso.framework.util.accessor.IAccessorFactory; /** * This is the core implementation of all entities in the application. Instances * of this class serve as handlers for proxies representing the entities. * * @author Vincent Vandenschrick */ public class BasicEntityInvocationHandler extends AbstractComponentInvocationHandler { private static final long serialVersionUID = 6078989823404409653L; private final IComponentPropertyStore properties; private int hashCode; /** * Constructs a new {@code BasicEntityInvocationHandler} instance. * * @param entityDescriptor * The descriptor of the proxy entity. * @param inlineComponentFactory * the factory used to create inline components. * @param collectionFactory * The factory used to create empty entity collections from * collection getters. * @param accessorFactory * The factory used to access proxy properties. * @param extensionFactory * The factory used to create entity extensions based on their * classes. */ protected BasicEntityInvocationHandler(IComponentDescriptor<IEntity> entityDescriptor, IComponentFactory inlineComponentFactory, IComponentCollectionFactory collectionFactory, IAccessorFactory accessorFactory, IComponentExtensionFactory extensionFactory) { super(entityDescriptor, inlineComponentFactory, collectionFactory, accessorFactory, extensionFactory); this.properties = createPropertyStore(); this.hashCode = -1; } /** * Handles methods invocations on the entity proxy. Either : <li>delegates to * one of its extension if the accessed property is registered as being part * of an extension <li>handles property access internally * <p> * {@inheritDoc} */ @Override public synchronized Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if ("isPersistent".equals(methodName)) { return isPersistent(proxy); } return super.invoke(proxy, method, args); } /** * Is persistent. * * @param proxy * the proxy * @return the boolean */ protected boolean isPersistent(Object proxy) { Integer version = ((IEntity) proxy).getVersion(); return version != null && !IEntity.DELETED_VERSION.equals(version); } /** * {@inheritDoc} */ @Override protected boolean computeEquals(IComponent proxy, Object another) { if (proxy == another) { return true; } Object id = straightGetProperty(proxy, IEntity.ID); if (id == null) { return false; } if (another instanceof IEntity) { Object otherId; Class<?> otherContract; if (Proxy.isProxyClass(another.getClass()) && Proxy.getInvocationHandler( another) instanceof BasicEntityInvocationHandler) { BasicEntityInvocationHandler otherInvocationHandler = (BasicEntityInvocationHandler) Proxy.getInvocationHandler( another); otherContract = otherInvocationHandler.getComponentContract(); otherId = otherInvocationHandler.straightGetProperty(proxy, IEntity.ID); } else { otherContract = ((IEntity) another).getComponentContract(); otherId = ((IEntity) another).getId(); } return new EqualsBuilder().append(getComponentContract(), otherContract).append(id, otherId).isEquals(); } return false; } /** * {@inheritDoc} */ @Override protected int computeHashCode(IComponent proxy) { if (hashCode == -1) { Object id = straightGetProperty(proxy, IEntity.ID); if (id == null) { throw new NullPointerException("Id must be assigned on the entity before its hashcode can be used."); } hashCode = new HashCodeBuilder(3, 17).append(id).toHashCode(); } return hashCode; } /** * {@inheritDoc} */ @Override protected IComponent decorateReferent(IComponent referent, IComponentDescriptor<? extends IComponent> referentDescriptor) { return referent; } /** * {@inheritDoc} */ @Override protected Object retrievePropertyValue(String propertyName) { return properties.get(propertyName); } /** * {@inheritDoc} */ @Override protected void storeProperty(String propertyName, Object propertyValue) { properties.set(propertyName, refinePropertyToStore(propertyValue)); } private IComponentPropertyStore createPropertyStore() { try { BasicComponentPropertyStore store = (BasicComponentPropertyStore) Class.forName( getComponentContract().getName() + "$State").newInstance(); store.setAccessorFactory(getAccessorFactory()); return store; } catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) { throw new ComponentException(ex, "Can not create component property store for " + getComponentContract().getName()); } } /** * {@inheritDoc} */ @Override protected void markDeleted(Object proxy) { ((IEntity) proxy).straightSetProperty(IEntity.VERSION, IEntity.DELETED_VERSION); super.markDeleted(proxy); } /** * Assigns raw property if the entity is not persistent yet. * * @param proxy * the proxy * @param propertyDescriptor * the property descriptor * @param translatedValue * the translated value */ @Override protected void invokeNlsOrRawSetter(Object proxy, IStringPropertyDescriptor propertyDescriptor, String translatedValue) { super.invokeNlsOrRawSetter(proxy, propertyDescriptor, translatedValue); if (!isPersistent(proxy)) { try { String rawPropertyName = propertyDescriptor.getName() + IComponentDescriptor.RAW_SUFFIX; getAccessorFactory().createPropertyAccessor(rawPropertyName, getComponentDescriptor().getComponentContract()) .setValue(proxy, translatedValue); } catch (IllegalAccessException | NoSuchMethodException ex) { throw new ComponentException(ex); } catch (InvocationTargetException ex) { if (ex.getCause() instanceof RuntimeException) { throw (RuntimeException) ex.getCause(); } throw new ComponentException(ex.getCause()); } } } }