/*
* 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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Collection;
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.ILifecycleCapable;
import org.jspresso.framework.model.component.IQueryComponent;
import org.jspresso.framework.model.component.basic.AbstractComponentFactory;
import org.jspresso.framework.model.component.basic.BasicComponentInvocationHandler;
import org.jspresso.framework.model.component.basic.BasicDelegatingComponentInvocationHandler;
import org.jspresso.framework.model.component.query.QueryComponent;
import org.jspresso.framework.model.descriptor.ICollectionPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptorRegistry;
import org.jspresso.framework.model.descriptor.IPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IQueryComponentDescriptorFactory;
import org.jspresso.framework.model.descriptor.IScalarPropertyDescriptor;
import org.jspresso.framework.model.descriptor.basic.BasicQueryComponentDescriptorFactory;
import org.jspresso.framework.model.entity.EntityException;
import org.jspresso.framework.model.entity.IEntity;
import org.jspresso.framework.model.entity.IEntityFactory;
import org.jspresso.framework.model.entity.IEntityLifecycleHandler;
import org.jspresso.framework.security.UserPrincipal;
import org.jspresso.framework.util.uid.IGUIDGenerator;
/**
* Default implementation of {@code IEntityFactory}. It creates standard java
* proxies which delegate to {@code BasicEntityInvocationHandler}s.
*
* @author Vincent Vandenschrick
*/
public class BasicProxyEntityFactory extends AbstractComponentFactory implements IEntityFactory {
private IComponentCollectionFactory componentCollectionFactory;
private IComponentExtensionFactory componentExtensionFactory;
private IComponentDescriptorRegistry componentDescriptorRegistry;
private IQueryComponentDescriptorFactory queryComponentDescriptorFactory;
private IGUIDGenerator<?> entityGUIDGenerator;
/**
* {@inheritDoc}
*/
@Override
public final <T extends IEntity> T createEntityInstance(Class<T> entityContract) {
return createEntityInstance(entityContract, entityGUIDGenerator.generateGUID(), true);
}
/**
* Performs necessary post instantiation initialization.
*
* @param entity
* the instantiated entity.
*/
protected void initializeEntity(IEntity entity) {
initializeComponent(entity);
}
/**
* {@inheritDoc}
*/
@Override
public final <T extends IEntity> T createEntityInstance(Class<T> entityContract, Serializable id) {
return createEntityInstance(entityContract, id, false);
}
/**
* {@inheritDoc}
*/
@Override
public final <T extends IEntity> T createEntityInstance(Class<T> entityContract, Serializable id,
boolean performInitialization) {
final T createdEntity = createEntityInstance(entityContract, id, (Class<?>[]) null);
createdEntity.addPropertyChangeListener(IEntity.VERSION, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getOldValue() == null && evt.getNewValue() != null) {
createdEntity.firePropertyChange(IEntity.PERSISTENT, false, true);
} else if (evt.getOldValue() != null && evt.getNewValue() == null) {
createdEntity.firePropertyChange(IEntity.PERSISTENT, true, false);
}
}
});
if (performInitialization) {
initializeEntity(createdEntity);
}
return createdEntity;
}
/**
* Sets the entityGUIDGenerator.
*
* @param entityGUIDGenerator
* the entityGUIDGenerator to set.
*/
public void setEntityGUIDGenerator(IGUIDGenerator<?> entityGUIDGenerator) {
this.entityGUIDGenerator = entityGUIDGenerator;
}
/**
* Creates the entity proxy invocation handler.
*
* @param entityDescriptor
* the entity descriptor.
* @return the entity proxy invocation handler.
*/
protected InvocationHandler createEntityInvocationHandler(IComponentDescriptor<IEntity> entityDescriptor) {
return new BasicEntityInvocationHandler(entityDescriptor, this, componentCollectionFactory, getAccessorFactory(),
componentExtensionFactory);
}
/**
* Gets the entity lifecycle handler.
*
* @return the entity lifecycle handler.
*/
protected IEntityLifecycleHandler getEntityLifecycleHandler() {
return null;
}
@SuppressWarnings("unchecked")
private <T extends IEntity> T createEntityInstance(Class<T> entityContract, Serializable id,
Class<?>... extraInterfaces) {
T entity;
if (entityContract.isInterface()) {
IComponentDescriptor<IEntity> entityDescriptor = (IComponentDescriptor<IEntity>) getComponentDescriptor(entityContract);
if (entityDescriptor.isPurelyAbstract()) {
throw new EntityException(entityDescriptor.getName() + " is purely abstract. It cannot be instantiated.");
}
InvocationHandler entityHandler = createEntityInvocationHandler(entityDescriptor);
Class<?>[] implementedClasses;
if (extraInterfaces != null) {
implementedClasses = new Class<?>[extraInterfaces.length + 2];
implementedClasses[0] = entityDescriptor.getComponentContract();
implementedClasses[1] = ILifecycleCapable.class;
System.arraycopy(extraInterfaces, 0, implementedClasses, 2, extraInterfaces.length);
} else {
implementedClasses = new Class<?>[2];
implementedClasses[0] = entityDescriptor.getComponentContract();
implementedClasses[1] = ILifecycleCapable.class;
}
entity = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), implementedClasses,
entityHandler);
} else {
try {
entity = entityContract.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
throw new EntityException(ex, "Could not instantiate entity " + entityContract.getName());
}
}
entity.straightSetProperty(IEntity.ID, id);
return entity;
}
/**
* Gets the entityGUIDGenerator.
*
* @return the entityGUIDGenerator.
*/
protected IGUIDGenerator<?> getEntityGUIDGenerator() {
return entityGUIDGenerator;
}
/**
* {@inheritDoc}
*/
@Override
public <T extends IComponent> T createComponentInstance(Class<T> componentContract) {
return createComponentInstance(componentContract, null);
}
/**
* {@inheritDoc}
*/
@Override
public <T extends IComponent> T createComponentInstance(Class<T> componentContract, Object delegate) {
T createdComponent = createComponentInstance(componentContract, delegate, (Class<?>[]) null);
initializeComponent(createdComponent);
return createdComponent;
}
/**
* Performs necessary post instantiation initialization.
*
* @param component
* the instantiated component.
*/
protected void initializeComponent(IComponent component) {
IComponentDescriptor<?> componentDescriptor = getComponentDescriptor(component.getComponentContract());
for (IPropertyDescriptor propertyDescriptor : componentDescriptor.getPropertyDescriptors()) {
if (propertyDescriptor instanceof ICollectionPropertyDescriptor<?>) {
component.straightSetProperty(propertyDescriptor.getName(),
componentCollectionFactory.createComponentCollection(propertyDescriptor.getModelType()));
} else if (propertyDescriptor instanceof IScalarPropertyDescriptor) {
Object defaultValue = ((IScalarPropertyDescriptor) propertyDescriptor).getDefaultValue();
if (defaultValue != null) {
defaultValue = propertyDescriptor.interceptSetter(component, defaultValue);
propertyDescriptor.preprocessSetter(component, defaultValue);
component.straightSetProperty(propertyDescriptor.getName(),
((IScalarPropertyDescriptor) propertyDescriptor).getDefaultValue());
}
}
}
if (component instanceof ILifecycleCapable) {
((ILifecycleCapable) component).onCreate(this, getPrincipal(), getEntityLifecycleHandler());
}
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public IQueryComponent createQueryComponentInstance(Class<? extends IComponent> componentContract) {
return new QueryComponent(getQueryComponentDescriptorFactory().createQueryComponentDescriptor(
(IComponentDescriptor<IComponent>) getComponentDescriptor(componentContract)), this);
}
/**
* {@inheritDoc}
*/
@Override
public IComponentDescriptor<?> getComponentDescriptor(Class<?> componentContract) {
return componentDescriptorRegistry.getComponentDescriptor(componentContract);
}
/**
* {@inheritDoc}
*/
@Override
public Collection<IComponentDescriptor<?>> getComponentDescriptors() {
return componentDescriptorRegistry.getComponentDescriptors();
}
/**
* Sets the componentCollectionFactory property.
*
* @param componentCollectionFactory
* the componentCollectionFactory to set.
*/
public void setComponentCollectionFactory(IComponentCollectionFactory componentCollectionFactory) {
this.componentCollectionFactory = componentCollectionFactory;
}
/**
* Sets the componentDescriptorRegistry.
*
* @param componentDescriptorRegistry
* the componentDescriptorRegistry to set.
*/
public void setComponentDescriptorRegistry(IComponentDescriptorRegistry componentDescriptorRegistry) {
this.componentDescriptorRegistry = componentDescriptorRegistry;
}
/**
* Sets the componentExtensionFactory property.
*
* @param componentExtensionFactory
* the componentCollectionFactory to set.
*/
public void setComponentExtensionFactory(IComponentExtensionFactory componentExtensionFactory) {
this.componentExtensionFactory = componentExtensionFactory;
}
/**
* Creates the component proxy invocation handler.
*
* @param componentDescriptor
* the component descriptor.
* @return the component proxy invocation handler.
*/
protected InvocationHandler createComponentInvocationHandler(IComponentDescriptor<IComponent> componentDescriptor) {
return new BasicComponentInvocationHandler(componentDescriptor, this, componentCollectionFactory,
getAccessorFactory(), componentExtensionFactory);
}
/**
* Gets the componentCollectionFactory.
*
* @return the componentCollectionFactory.
*/
protected IComponentCollectionFactory getComponentCollectionFactory() {
return componentCollectionFactory;
}
/**
* Gets the componentExtensionFactory.
*
* @return the componentExtensionFactory.
*/
protected IComponentExtensionFactory getComponentExtensionFactory() {
return componentExtensionFactory;
}
/**
* Gets the principal using the factory.
*
* @return the principal using the factory.
*/
protected UserPrincipal getPrincipal() {
return null;
}
@SuppressWarnings("unchecked")
private <T extends IComponent> T createComponentInstance(Class<T> componentContract, Object delegate,
Class<?>... extraInterfaces) {
if (IEntity.class.isAssignableFrom(componentContract)) {
throw new IllegalArgumentException(componentContract.getName()
+ " is an entity contract. You should use createEntityInstance instead.");
}
IComponentDescriptor<IComponent> componentDescriptor = (IComponentDescriptor<IComponent>) componentDescriptorRegistry
.getComponentDescriptor(componentContract);
InvocationHandler componentHandler;
if (delegate != null) {
componentHandler = createDelegatingComponentInvocationHandler(componentDescriptor, delegate);
} else {
componentHandler = createComponentInvocationHandler(componentDescriptor);
}
Class<?>[] implementedClasses;
if (extraInterfaces != null) {
implementedClasses = new Class<?>[extraInterfaces.length + 2];
implementedClasses[0] = componentDescriptor.getComponentContract();
implementedClasses[1] = ILifecycleCapable.class;
System.arraycopy(extraInterfaces, 0, implementedClasses, 2, extraInterfaces.length);
} else {
implementedClasses = new Class<?>[2];
implementedClasses[0] = componentDescriptor.getComponentContract();
implementedClasses[1] = ILifecycleCapable.class;
}
T component = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), implementedClasses,
componentHandler);
return component;
}
private InvocationHandler createDelegatingComponentInvocationHandler(
IComponentDescriptor<IComponent> componentDescriptor, Object delegate) {
return new BasicDelegatingComponentInvocationHandler(delegate, this, componentDescriptor,
componentCollectionFactory, getAccessorFactory(), componentExtensionFactory);
}
/**
* Gets the queryComponentDescriptorFactory.
*
* @return the queryComponentDescriptorFactory.
*/
protected IQueryComponentDescriptorFactory getQueryComponentDescriptorFactory() {
if (queryComponentDescriptorFactory == null) {
queryComponentDescriptorFactory = new BasicQueryComponentDescriptorFactory();
}
return queryComponentDescriptorFactory;
}
/**
* Sets the queryComponentDescriptorFactory.
*
* @param queryComponentDescriptorFactory
* the queryComponentDescriptorFactory to set.
*/
public void setQueryComponentDescriptorFactory(IQueryComponentDescriptorFactory queryComponentDescriptorFactory) {
this.queryComponentDescriptorFactory = queryComponentDescriptorFactory;
}
}