blob: 7228c4163cfdf16f56e903ec93871213180e4ebf [file] [log] [blame]
/*
* Copyright (c) 2008-2009, Rickard Öberg. All Rights Reserved.
* Copyright (c) 2009-2013, Niclas Hedhman. All Rights Reserved.
* Copyright (c) 2014, Paul Merlin. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.qi4j.runtime.entity;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Set;
import org.qi4j.api.association.Association;
import org.qi4j.api.association.AssociationDescriptor;
import org.qi4j.api.association.AssociationStateDescriptor;
import org.qi4j.api.association.ManyAssociation;
import org.qi4j.api.association.NamedAssociation;
import org.qi4j.api.composite.CompositeDescriptor;
import org.qi4j.api.composite.CompositeInstance;
import org.qi4j.api.constraint.ConstraintViolationException;
import org.qi4j.api.entity.EntityComposite;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.entity.Identity;
import org.qi4j.api.entity.LifecycleException;
import org.qi4j.api.unitofwork.NoSuchEntityException;
import org.qi4j.api.unitofwork.UnitOfWork;
import org.qi4j.api.unitofwork.UnitOfWorkException;
import org.qi4j.runtime.composite.CompositeMethodInstance;
import org.qi4j.runtime.composite.MixinsInstance;
import org.qi4j.runtime.structure.ModuleUnitOfWork;
import org.qi4j.spi.entity.EntityState;
import org.qi4j.spi.entity.EntityStatus;
import org.qi4j.spi.module.ModuleSpi;
/**
* Entity instance
*/
public final class EntityInstance
implements CompositeInstance, MixinsInstance
{
public static EntityInstance entityInstanceOf( EntityComposite composite )
{
return (EntityInstance) Proxy.getInvocationHandler( composite );
}
private final EntityComposite proxy;
private final ModuleUnitOfWork uow;
private final ModuleSpi moduleInstance;
private final EntityModel entityModel;
private final EntityReference identity;
private final EntityState entityState;
private Object[] mixins;
private EntityStateInstance state;
public EntityInstance( ModuleUnitOfWork uow,
ModuleSpi moduleInstance,
EntityModel entityModel,
EntityState entityState
)
{
this.uow = uow;
this.moduleInstance = moduleInstance;
this.entityModel = entityModel;
this.identity = entityState.identity();
this.entityState = entityState;
proxy = (EntityComposite) entityModel.newProxy( this );
}
@Override
public Object invoke( Object proxy, Method method, Object[] args )
throws Throwable
{
return entityModel.invoke( this, this.proxy, method, args, moduleInstance );
}
public EntityReference identity()
{
return identity;
}
@Override
@SuppressWarnings( "unchecked" )
public <T> T proxy()
{
return (T) proxy;
}
@Override
public CompositeDescriptor descriptor()
{
return entityModel;
}
@Override
public <T> T newProxy( Class<T> mixinType )
throws IllegalArgumentException
{
return entityModel.newProxy( this, mixinType );
}
@Override
public Object invokeComposite( Method method, Object[] args )
throws Throwable
{
return entityModel.invoke( this, proxy, method, args, moduleInstance );
}
@Override
public <T> T metaInfo( Class<T> infoType )
{
return entityModel.metaInfo( infoType );
}
public EntityModel entityModel()
{
return entityModel;
}
@Override
public Iterable<Class<?>> types()
{
return entityModel.types();
}
@Override
public ModuleSpi module()
{
return moduleInstance;
}
public UnitOfWork unitOfWork()
{
return uow;
}
public EntityState entityState()
{
return entityState;
}
@Override
public EntityStateInstance state()
{
if( state == null )
{
initState();
}
return state;
}
public EntityStatus status()
{
return entityState.status();
}
@Override
public Object invoke( Object composite, Object[] params, CompositeMethodInstance methodInstance )
throws Throwable
{
if( mixins == null )
{
initState();
}
Object mixin = methodInstance.getMixinFrom( mixins );
if( mixin == null )
{
mixin = entityModel.newMixin( mixins, state, this, methodInstance.method() );
}
return methodInstance.invoke( proxy, params, mixin );
}
@Override
public Object invokeObject( Object proxy, Object[] args, Method method )
throws Throwable
{
return method.invoke( this, args );
}
private void initState()
{
if( !uow.isOpen() )
{
throw new UnitOfWorkException( "Unit of work has been closed" );
}
if( status() == EntityStatus.REMOVED )
{
throw new NoSuchEntityException( identity, entityModel.types(), unitOfWork().usecase() );
}
mixins = entityModel.newMixinHolder();
state = new EntityStateInstance( entityModel.state(), uow, entityState );
}
@Override
public int hashCode()
{
return identity.hashCode();
}
@Override
public boolean equals( Object o )
{
try
{
Identity other = ( (Identity) o );
return other != null && other.identity().get().equals( identity.identity() );
}
catch( ClassCastException e )
{
return false;
}
}
@Override
public String toString()
{
return identity.toString();
}
public void remove( UnitOfWork unitOfWork )
throws LifecycleException
{
invokeRemove();
removeAggregatedEntities( unitOfWork );
entityState.remove();
mixins = null;
}
public void invokeCreate()
{
lifecyleInvoke( true );
}
private void invokeRemove()
{
lifecyleInvoke( false );
}
private void lifecyleInvoke( boolean create )
{
if( mixins == null )
{
initState();
}
entityModel.invokeLifecycle( create, mixins, this, state );
}
private void removeAggregatedEntities( UnitOfWork unitOfWork )
{
// Calculate aggregated Entities
AssociationStateDescriptor stateDescriptor = entityModel.state();
Set<Object> aggregatedEntities = new HashSet<>();
Iterable<? extends AssociationDescriptor> associations = stateDescriptor.associations();
for( AssociationDescriptor association : associations )
{
if( association.isAggregated() )
{
Association<?> assoc = state.associationFor( association.accessor() );
Object aggregatedEntity = assoc.get();
if( aggregatedEntity != null )
{
aggregatedEntities.add( aggregatedEntity );
}
}
}
Iterable<? extends AssociationDescriptor> manyAssociations = stateDescriptor.manyAssociations();
for( AssociationDescriptor association : manyAssociations )
{
if( association.isAggregated() )
{
ManyAssociation<?> manyAssoc = state.manyAssociationFor( association.accessor() );
for( Object entity : manyAssoc )
{
aggregatedEntities.add( entity );
}
}
}
Iterable<? extends AssociationDescriptor> namedAssociations = stateDescriptor.namedAssociations();
for( AssociationDescriptor association : namedAssociations )
{
if( association.isAggregated() )
{
NamedAssociation<?> namedAssoc = state.namedAssociationFor( association.accessor() );
for( String name : namedAssoc )
{
aggregatedEntities.add( namedAssoc.get( name ) );
}
}
}
// Remove aggregated Entities
for( Object aggregatedEntity : aggregatedEntities )
{
unitOfWork.remove( aggregatedEntity );
}
}
public void checkConstraints()
{
try
{
state.checkConstraints();
}
catch( ConstraintViolationException e )
{
throw new ConstraintViolationException( identity.identity(), entityModel.types(), e.mixinTypeName(), e.methodName(), e
.constraintViolations() );
}
}
}