blob: 47fa360ba2538bd5a5093df9988fcf155004eddd [file] [log] [blame]
/*
* Copyright (c) 2007-2011, Rickard Öberg. All Rights Reserved.
* Copyright (c) 2007-2012, Niclas Hedhman. All Rights Reserved.
* Copyright (c) 2013-2015, 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.apache.zest.api.unitofwork;
import java.util.Map;
import org.apache.zest.api.association.AssociationDescriptor;
import org.apache.zest.api.composite.AmbiguousTypeException;
import org.apache.zest.api.entity.EntityBuilder;
import org.apache.zest.api.entity.EntityReference;
import org.apache.zest.api.entity.Identity;
import org.apache.zest.api.entity.LifecycleException;
import org.apache.zest.api.property.PropertyDescriptor;
import org.apache.zest.api.query.Query;
import org.apache.zest.api.query.QueryBuilder;
import org.apache.zest.api.structure.MetaInfoHolder;
import org.apache.zest.api.usecase.Usecase;
import org.apache.zest.functional.Function;
/**
* All operations on entities goes through an UnitOfWork.
* <p>
* A UnitOfWork allows you to access
* Entities and work with them. All modifications to Entities are recorded by the UnitOfWork,
* and at the end they may be sent to the underlying EntityStore by calling complete(). If the
* UoW was read-only you may instead simply discard() it.
* </p>
* <p>
* A UoW differs from a traditional Transaction in the sense that it is not tied at all to the underlying
* storage resource. Because of this there is no timeout on a UoW. It can be very short or very long.
* Another difference is that if a call to complete() fails, and the cause is validation errors in the
* Entities of the UoW, then these can be corrected and the UoW retried. By contrast, when a Transaction
* commit fails, then the whole transaction has to be done from the beginning again.
* </p>
* <p>
* A UoW can be associated with a Usecase. A Usecase describes the metainformation about the process
* to be performed by the UoW.
* </p>
* <p>
* If a code block that uses a UoW throws an exception you need to ensure that this is handled properly,
* and that the UoW is closed before returning. Because discard() is a no-op if the UoW is closed, we therefore
* recommend the following template to be used:
* </p>
* <pre>
* UnitOfWork uow = module.newUnitOfWork();
* try
* {
* ...
* uow.complete();
* }
* finally
* {
* uow.discard();
* }
* </pre>
* <p>
* This ensures that in the happy case the UoW is completed, and if any exception is thrown the UoW is discarded. After
* the UoW has completed the discard() method doesn't do anything, and so has no effect. You can choose to either add
* catch blocks for any exceptions, including exceptions from complete(), or skip them.
* </p>
* <p>
* Since 2.1 you can leverage Java 7 Automatic Resource Management (ie. Try With Resources) and use the following
* template instead:
* </p>
* <pre>
* try( UnitOfWork uow = module.newUnitOfWork() )
* {
* ...
* uow.complete();
* }
* </pre>
* <p>
* It has the very same effect than the template above but is shorter.</p>
*/
public interface UnitOfWork extends MetaInfoHolder, AutoCloseable
{
/**
* Get the UnitOfWorkFactory that this UnitOfWork was created from.
*
* @return The UnitOfWorkFactory instance that was used to create this UnitOfWork.
*/
UnitOfWorkFactory unitOfWorkFactory();
long currentTime();
/**
* Get the Usecase for this UnitOfWork
*
* @return the Usecase
*/
Usecase usecase();
void setMetaInfo( Object metaInfo );
<T> Query<T> newQuery( QueryBuilder<T> queryBuilder );
// DataSet newDataSetBuilder(Specification<?>... constraints);
/**
* Create a new Entity which implements the given mixin type.
* <p>
* An EntityComposite
* will be chosen according to what has been registered and the visibility rules
* for Modules and Layers will be considered. If several
* EntityComposites implement the type then an AmbiguousTypeException will be thrown.
* </p>
* <p>
* The identity of the Entity will be generated by the IdentityGenerator of the Module of the EntityComposite.
* </p>
*
* @param type the mixin type that the EntityComposite must implement
*
* @return a new Entity
*
* @throws EntityTypeNotFoundException if no EntityComposite type of the given mixin type has been registered
* @throws AmbiguousTypeException If several mixins implement the given type
* @throws LifecycleException if the entity cannot be created
*/
<T> T newEntity( Class<T> type )
throws EntityTypeNotFoundException, AmbiguousTypeException, LifecycleException;
/**
* Create a new Entity which implements the given mixin type. An EntityComposite
* will be chosen according to what has been registered and the visibility rules
* for Modules and Layers will be considered. If several
* EntityComposites implement the type then an AmbiguousTypeException will be thrown.
*
* @param type the mixin type that the EntityComposite must implement
* @param identity the identity of the new Entity
*
* @return a new Entity
*
* @throws EntityTypeNotFoundException if no EntityComposite type of the given mixin type has been registered
* @throws AmbiguousTypeException If several mixins implement the given type
* @throws LifecycleException if the entity cannot be created
*/
<T> T newEntity( Class<T> type, String identity )
throws EntityTypeNotFoundException, AmbiguousTypeException, LifecycleException;
/**
* Create a new EntityBuilder for an EntityComposite which implements the given mixin type. An EntityComposite
* will be chosen according to what has been registered and the visibility rules
* for Modules and Layers will be considered. If several
* EntityComposites implement the type then an AmbiguousTypeException will be thrown.
*
* @param type the mixin type that the EntityComposite must implement
*
* @return a new EntityBuilder
*
* @throws EntityTypeNotFoundException if no EntityComposite type of the given mixin type has been registered
* @throws AmbiguousTypeException If several mixins implement the given type
*/
<T> EntityBuilder<T> newEntityBuilder( Class<T> type )
throws EntityTypeNotFoundException, AmbiguousTypeException;
/**
* Create a new EntityBuilder for an EntityComposite which implements the given mixin type. An EntityComposite
* will be chosen according to what has been registered and the visibility rules
* for Modules and Layers will be considered. If several
* mixins implement the type then an AmbiguousTypeException will be thrown.
*
* @param type the mixin type that the EntityComposite must implement
* @param identity the identity of the new Entity
*
* @return a new EntityBuilder
*
* @throws EntityTypeNotFoundException if no EntityComposite type of the given mixin type has been registered
* @throws AmbiguousTypeException If several mixins implement the given type
*/
<T> EntityBuilder<T> newEntityBuilder( Class<T> type, String identity )
throws EntityTypeNotFoundException, AmbiguousTypeException;
/**
* Create a new EntityBuilder for an EntityComposite wich implements the given mixin type starting with the given
* state.
* <p>
* An EntityComposite will be chosen according to what has been registered and the visibility rules for Modules and
* Layers will be considered.
*
* @param <T> Entity type
* @param type Entity type
* @param propertyFunction a function providing the state of properties
* @param associationFunction a function providing the state of associations
* @param manyAssociationFunction a function providing the state of many associations
* @param namedAssociationFunction a function providing the state of named associations
*
* @return a new EntityBuilder starting with the given state
*
* @throws EntityTypeNotFoundException if no EntityComposite type of the given mixin type has been registered
* @throws AmbiguousTypeException If several mixins implement the given type
*/
<T> EntityBuilder<T> newEntityBuilderWithState( Class<T> type,
Function<PropertyDescriptor, Object> propertyFunction,
Function<AssociationDescriptor, EntityReference> associationFunction,
Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction,
Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction )
throws EntityTypeNotFoundException, AmbiguousTypeException;
/**
* Create a new EntityBuilder for an EntityComposite wich implements the given mixin type starting with the given
* state.
* <p>
* An EntityComposite will be chosen according to what has been registered and the visibility rules for Modules and
* Layers will be considered.
*
* @param <T> Entity type
* @param type Entity type
* @param identity the identity of the new Entity
* @param propertyFunction a function providing the state of properties
* @param associationFunction a function providing the state of associations
* @param manyAssociationFunction a function providing the state of many associations
* @param namedAssociationFunction a function providing the state of named associations
*
* @return a new EntityBuilder starting with the given state
*
* @throws EntityTypeNotFoundException If no mixins implements the given type
* @throws AmbiguousTypeException If several mixins implement the given type
*/
<T> EntityBuilder<T> newEntityBuilderWithState( Class<T> type, String identity,
Function<PropertyDescriptor, Object> propertyFunction,
Function<AssociationDescriptor, EntityReference> associationFunction,
Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction,
Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction )
throws EntityTypeNotFoundException, AmbiguousTypeException;
/**
* Find an Entity of the given mixin type with the give identity. This
* method verifies that it exists by asking the underlying EntityStore.
*
* @param type of the entity
* @param identity of the entity
*
* @return the entity
*
* @throws EntityTypeNotFoundException if no entity type could be found
* @throws NoSuchEntityException if the entity could not be found
*/
<T> T get( Class<T> type, String identity )
throws EntityTypeNotFoundException, NoSuchEntityException;
/**
* If you have a reference to an Entity from another
* UnitOfWork and want to create a reference to it in this
* UnitOfWork, then call this method.
*
* @param entity the Entity to be dereferenced
*
* @return an Entity from this UnitOfWork
*
* @throws EntityTypeNotFoundException if no entity type could be found
*/
<T> T get( T entity )
throws EntityTypeNotFoundException;
/**
* Remove the given Entity.
*
* @param entity the Entity to be removed.
*
* @throws LifecycleException if the entity could not be removed
*/
void remove( Object entity )
throws LifecycleException;
/**
* Complete this UnitOfWork. This will send all the changes down to the underlying
* EntityStore's.
*
* @throws UnitOfWorkCompletionException if the UnitOfWork could not be completed
* @throws ConcurrentEntityModificationException if entities have been modified by others
*/
void complete()
throws UnitOfWorkCompletionException, ConcurrentEntityModificationException;
/**
* Discard this UnitOfWork. Use this if a failure occurs that you cannot handle,
* or if the usecase was of a read-only character. This is a no-op of the UnitOfWork
* is already closed.
*/
void discard();
/**
* Discard this UnitOfWork. Use this if a failure occurs that you cannot handle,
* or if the usecase was of a read-only character. This is a no-op of the UnitOfWork
* is already closed. This simply call the {@link #discard()} method and is an
* implementation of the {@link AutoCloseable} interface providing Try With Resources
* support for UnitOfWork.
*/
@Override
public void close();
/**
* Check if the UnitOfWork is open. It is closed after either complete() or discard()
* methods have been called successfully.
*
* @return true if the UnitOfWork is open.
*/
boolean isOpen();
/**
* Check if the UnitOfWork is paused. It is not paused after it has been create through the
* UnitOfWorkFactory, and it can be paused by calling {@link #pause()} and then resumed by calling
* {@link #resume()}.
*
* @return true if this UnitOfWork has been paused.
*/
boolean isPaused();
/**
* Pauses this UnitOfWork.
* <p>
* Calling this method will cause the underlying UnitOfWork to become the current UnitOfWork until the
* the resume() method is called. It is the client's responsibility not to drop the reference to this
* UnitOfWork while being paused.
* </p>
*/
void pause();
/**
* Resumes this UnitOfWork to again become the current UnitOfWork.
*/
void resume();
/**
* Register a callback. Callbacks are invoked when the UnitOfWork
* is completed or discarded.
*
* @param callback a callback to be registered with this UnitOfWork
*/
void addUnitOfWorkCallback( UnitOfWorkCallback callback );
/**
* Unregister a callback. Callbacks are invoked when the UnitOfWork
* is completed or discarded.
*
* @param callback a callback to be unregistered with this UnitOfWork
*/
void removeUnitOfWorkCallback( UnitOfWorkCallback callback );
/**
* Converts the provided Entity to a Value of the same type.
* This is a convenience method to convert an EntityComposite to a ValueComposite.
* <p>
* All Property values are transferred across as-is, and the Association, ManyAssociation
* and NamedAssociatino values are kept in the ValueComposite as EntityReferences
* until they are dereferenced (get() and other methods), and IF a UnitOfWork is
* present at dereferencing the corresponding EntityCompoiste is retrieved from the
* EntityStore. If there is not an UnitOfWork present, an exception is thrown.
* </p>
* <p>
* For this to work, the Composites (both Entity and Value) must not declare the
* EntityComposite and ValueComposite super types, but rely on the declaration in
* the assembly, and also extend the Identity supertype.
* </p>
* Example;
* <pre><code>
* public interface Person extends Identity { ... };
* public class MyAssembler
* {
* public void assemble( ModuleAssembly module )
* {
* module.values( Person.class );
* module.entities( Person.class );
* }
* }
* </code></pre>
*
* @param primaryType The shared type for which the properties and associations will
* be converted. Properties outside this type will be ignored.
* @param entityComposite The entity to be convered.
*/
<T extends Identity> T toValue( Class<T> primaryType, T entityComposite );
/**
* Converts the provided Value to an Entity of the same type.
* This is a convenience method to convert a ValueComposite to an EntityComposite.
* <p>
* All Property values are transferred across as-is (no deep copy in case mutable
* types (DISCOURAGED!) are used), and the Association, ManyAssociation
* and NamedAssociatino that were in the ValueComposite as EntityReferences are
* transferred into the EntityComposite correctly, and can be dereferenced.
* </p>
* <p>
* This method MUST be called within a UnitOfWork.
* </p>
* <p>
* If an Entity with the Identity in the ValueComposite already exists, then that
* Entity is updated with the values from the ValueComposite. If an Entity of
* that Identity doesn't exist and new one is created.
* </p>
* <p>
* For this to work, the Composites (both Entity and Value) must not declare the
* EntityComposite and ValueComposite super types, but rely on the declaration in
* the assembly, and also extend the Identity supertype.
* </p>
* Example;
* <pre><code>
* public interface Person extends Identity { ... };
* public class MyAssembler
* {
* public void assemble( ModuleAssembly module )
* {
* module.values( Person.class );
* module.entities( Person.class );
* }
* }
* </code></pre>
*
* @param primaryType The shared type for which the properties and associations will
* be converted. Properties outside this type will be ignored.
* @param valueComposite The Value to be convered into an Entity.
*/
<T extends Identity> T toEntity( Class<T> primaryType, T valueComposite );
}