/*
* Copyright (c) 2009, Rickard Öberg. All Rights Reserved.
* Copyright (c) 2013, Niclas Hedhman. All Rights Reserved.
* Copyright (c) 2013, 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.structure;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import org.qi4j.api.composite.Composite;
import org.qi4j.api.entity.EntityBuilder;
import org.qi4j.api.entity.EntityComposite;
import org.qi4j.api.entity.EntityReference;
import org.qi4j.api.entity.IdentityGenerator;
import org.qi4j.api.entity.LifecycleException;
import org.qi4j.api.query.Query;
import org.qi4j.api.query.QueryBuilder;
import org.qi4j.api.query.QueryExecutionException;
import org.qi4j.api.query.grammar.OrderBy;
import org.qi4j.api.service.NoSuchServiceException;
import org.qi4j.api.unitofwork.ConcurrentEntityModificationException;
import org.qi4j.api.unitofwork.EntityTypeNotFoundException;
import org.qi4j.api.unitofwork.NoSuchEntityException;
import org.qi4j.api.unitofwork.UnitOfWork;
import org.qi4j.api.unitofwork.UnitOfWorkCallback;
import org.qi4j.api.unitofwork.UnitOfWorkCompletionException;
import org.qi4j.api.unitofwork.UnitOfWorkFactory;
import org.qi4j.api.usecase.Usecase;
import org.qi4j.functional.Iterables;
import org.qi4j.functional.Specification;
import org.qi4j.runtime.entity.EntityInstance;
import org.qi4j.runtime.entity.EntityModel;
import org.qi4j.runtime.unitofwork.EntityBuilderInstance;
import org.qi4j.runtime.unitofwork.UnitOfWorkInstance;
import org.qi4j.spi.entity.EntityStatus;
import org.qi4j.spi.entitystore.EntityStore;
import org.qi4j.spi.query.EntityFinder;
import org.qi4j.spi.query.EntityFinderException;
import org.qi4j.spi.query.QueryBuilderSPI;
import org.qi4j.spi.query.QuerySource;
import static org.qi4j.api.entity.EntityReference.parseEntityReference;
import static org.qi4j.functional.Iterables.first;
/**
* JAVADOC
*/
public class ModuleUnitOfWork
implements UnitOfWork
{
// private static final QualifiedName IDENTITY_STATE_NAME;
//
// static
// {
// try
// {
// IDENTITY_STATE_NAME = QualifiedName.fromAccessor( Identity.class.getMethod( "identity" ) );
// }
// catch( NoSuchMethodException e )
// {
// throw new InternalError( "Qi4j Core Runtime codebase is corrupted. Contact Qi4j team: ModuleUnitOfWork" );
// }
// }
//
private final UnitOfWorkInstance uow;
private final ModuleInstance moduleInstance;
ModuleUnitOfWork( ModuleInstance moduleInstance, UnitOfWorkInstance uow )
{
this.moduleInstance = moduleInstance;
this.uow = uow;
}
public ModuleInstance module()
{
return moduleInstance;
}
public UnitOfWorkInstance instance()
{
return uow;
}
@Override
public UnitOfWorkFactory unitOfWorkFactory()
{
return moduleInstance;
}
@Override
public long currentTime()
{
return uow.currentTime();
}
@Override
public Usecase usecase()
{
return uow.usecase();
}
@Override
public <T> T metaInfo( Class<T> infoType )
{
return uow.metaInfo().get( infoType );
}
@Override
public void setMetaInfo( Object metaInfo )
{
uow.metaInfo().set( metaInfo );
}
@Override
@SuppressWarnings( {"raw", "unchecked"} )
public <T> Query<T> newQuery( QueryBuilder<T> queryBuilder )
{
QueryBuilderSPI queryBuilderSPI = (QueryBuilderSPI) queryBuilder;
return queryBuilderSPI.newQuery( new UoWQuerySource( this ) );
}
@Override
public <T> T newEntity( Class<T> type )
throws EntityTypeNotFoundException, LifecycleException
{
return newEntity( type, null );
}
@Override
public <T> T newEntity( Class<T> type, String identity )
throws EntityTypeNotFoundException, LifecycleException
{
return newEntityBuilder( type, identity ).newInstance();
}
@Override
public <T> EntityBuilder<T> newEntityBuilder( Class<T> type )
throws EntityTypeNotFoundException
{
return newEntityBuilder( type, null );
}
@Override
public <T> EntityBuilder<T> newEntityBuilder( Class<T> type, String identity )
throws EntityTypeNotFoundException
{
ModelModule<EntityModel> model = moduleInstance.typeLookup().lookupEntityModel( type );
if( model == null )
{
throw new EntityTypeNotFoundException( type.getName() );
}
EntityStore entityStore = model.module().entityStore();
// Generate id if necessary
if( identity == null )
{
IdentityGenerator idGen = model.module().identityGenerator();
if( idGen == null )
{
throw new NoSuchServiceException( IdentityGenerator.class.getName(), model.module().name() );
}
identity = idGen.generate( first( model.model().types() ) );
}
EntityBuilder<T> builder;
builder = new EntityBuilderInstance<>( model,
this,
uow.getEntityStoreUnitOfWork( entityStore, moduleInstance ),
identity );
return builder;
}
@Override
public <T> T get( Class<T> type, String identity )
throws EntityTypeNotFoundException, NoSuchEntityException
{
Iterable<ModelModule<EntityModel>> models = moduleInstance.typeLookup().lookupEntityModels( type );
if( !models.iterator().hasNext() )
{
throw new EntityTypeNotFoundException( type.getName() );
}
return uow.get( parseEntityReference( identity ), this, models, type );
}
@Override
@SuppressWarnings( "unchecked" )
public <T> T get( T entity )
throws EntityTypeNotFoundException
{
EntityComposite entityComposite = (EntityComposite) entity;
EntityInstance compositeInstance = EntityInstance.entityInstanceOf( entityComposite );
ModelModule<EntityModel> model = new ModelModule<>( compositeInstance.module(), compositeInstance.entityModel() );
Class<T> type = (Class<T>) first( compositeInstance.types() );
return uow.get( compositeInstance.identity(), this, Collections.singletonList( model ), type );
}
@Override
public void remove( Object entity )
throws LifecycleException
{
uow.checkOpen();
EntityComposite entityComposite = (EntityComposite) entity;
EntityInstance compositeInstance = EntityInstance.entityInstanceOf( entityComposite );
if( compositeInstance.status() == EntityStatus.NEW )
{
compositeInstance.remove( this );
uow.remove( compositeInstance.identity() );
}
else if( compositeInstance.status() == EntityStatus.LOADED || compositeInstance.status() == EntityStatus.UPDATED )
{
compositeInstance.remove( this );
}
else
{
throw new NoSuchEntityException( compositeInstance.identity(), compositeInstance.types() );
}
}
@Override
public void complete()
throws UnitOfWorkCompletionException, ConcurrentEntityModificationException
{
uow.complete();
}
@Override
public void discard()
{
uow.discard();
}
@Override
public void close()
{
discard();
}
@Override
public boolean isOpen()
{
return uow.isOpen();
}
@Override
public boolean isPaused()
{
return uow.isPaused();
}
@Override
public void pause()
{
uow.pause();
}
@Override
public void resume()
{
uow.resume();
}
@Override
public void addUnitOfWorkCallback( UnitOfWorkCallback callback )
{
uow.addUnitOfWorkCallback( callback );
}
@Override
public void removeUnitOfWorkCallback( UnitOfWorkCallback callback )
{
uow.removeUnitOfWorkCallback( callback );
}
@Override
public boolean equals( Object o )
{
if( this == o )
{
return true;
}
if( o == null || getClass() != o.getClass() )
{
return false;
}
ModuleUnitOfWork that = (ModuleUnitOfWork) o;
return uow.equals( that.uow );
}
@Override
public int hashCode()
{
return uow.hashCode();
}
@Override
public String toString()
{
return uow.toString();
}
public void addEntity( EntityInstance instance )
{
uow.addEntity( instance );
}
private static class UoWQuerySource implements QuerySource
{
private final ModuleUnitOfWork moduleUnitOfWork;
private UoWQuerySource( ModuleUnitOfWork moduleUnitOfWork )
{
this.moduleUnitOfWork = moduleUnitOfWork;
}
@Override
public <T> T find( Class<T> resultType,
Specification<Composite> whereClause,
Iterable<OrderBy> orderBySegments,
Integer firstResult,
Integer maxResults,
Map<String, Object> variables
)
{
final EntityFinder entityFinder = moduleUnitOfWork.module().findService( EntityFinder.class ).get();
try
{
final EntityReference foundEntity = entityFinder.findEntity( resultType, whereClause, variables == null ? Collections
.<String, Object>emptyMap() : variables );
if( foundEntity != null )
{
try
{
return moduleUnitOfWork.get( resultType, foundEntity.identity() );
}
catch( NoSuchEntityException e )
{
return null; // Index is out of sync - entity has been removed
}
}
// No entity was found
return null;
}
catch( EntityFinderException e )
{
throw new QueryExecutionException( "Finder caused exception", e );
}
}
@Override
public <T> long count( Class<T> resultType,
Specification<Composite> whereClause,
Iterable<OrderBy> orderBySegments,
Integer firstResult,
Integer maxResults,
Map<String, Object> variables
)
{
final EntityFinder entityFinder = moduleUnitOfWork.module().findService( EntityFinder.class ).get();
try
{
return entityFinder.countEntities( resultType, whereClause, variables == null ? Collections.<String, Object>emptyMap() : variables );
}
catch( EntityFinderException e )
{
e.printStackTrace();
return 0;
}
}
@Override
public <T> Iterator<T> iterator( final Class<T> resultType,
Specification<Composite> whereClause,
Iterable<OrderBy> orderBySegments,
Integer firstResult,
Integer maxResults,
Map<String, Object> variables
)
{
final EntityFinder entityFinder = moduleUnitOfWork.module().findService( EntityFinder.class ).get();
try
{
final Iterator<EntityReference> foundEntities = entityFinder.findEntities( resultType,
whereClause,
Iterables.toArray( OrderBy.class, orderBySegments ),
firstResult,
maxResults,
variables == null ? Collections
.<String, Object>emptyMap() : variables )
.iterator();
return new Iterator<T>()
{
@Override
public boolean hasNext()
{
return foundEntities.hasNext();
}
@Override
public T next()
{
final EntityReference foundEntity = foundEntities.next();
try
{
return moduleUnitOfWork.get( resultType, foundEntity.identity() );
}
catch( NoSuchEntityException e )
{
// Index is out of sync - entity has been removed
return null;
}
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
catch( EntityFinderException e )
{
throw new QueryExecutionException( "Query '" + toString() + "' could not be executed", e );
}
}
@Override
public String toString()
{
return "UnitOfWork( " + moduleUnitOfWork.usecase().name() + " )";
}
}
}