http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWork.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWork.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWork.java new file mode 100644 index 0000000..47fa360 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWork.java @@ -0,0 +1,429 @@ +/* + * 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 ); + + +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCallback.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCallback.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCallback.java new file mode 100644 index 0000000..af0f987 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCallback.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. 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; + +/** + * Callback interface for UnitOfWork completion or discard. Implementations + * of this interface can be registered through {@link UnitOfWork#addUnitOfWorkCallback(UnitOfWorkCallback)}. + * + * If Entities implement this interface they will also receive invocations of this callback interface. + */ +public interface UnitOfWorkCallback +{ + /** + * This is called before the completion of the UnitOfWork. + * The callback may do any validation checks and throw + * UnitOfWorkCompletionException if there is any reason + * why the UnitOfWork is not in a valid state to be completed. + * + * @throws UnitOfWorkCompletionException + */ + void beforeCompletion() + throws UnitOfWorkCompletionException; + + /** + * This is called after the completion or discarding + * of the UnitOfWork. The callback may do any cleanup + * necessary related to the UnitOfWork. Note that the + * UnitOfWork is no longer active when this method is + * called, so no methods on it may be invoked. + * + * @param status + */ + void afterCompletion( UnitOfWorkStatus status ); + + enum UnitOfWorkStatus + { + COMPLETED, DISCARDED + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCompletionException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCompletionException.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCompletionException.java new file mode 100644 index 0000000..092fc62 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkCompletionException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. 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; + +/** + * When an attempt to {@link UnitOfWork#complete()} an UnitOfWork + * fails, this exception will be thrown. + */ +public class UnitOfWorkCompletionException + extends Exception +{ + private static final long serialVersionUID = 6531642131384516904L; + + public UnitOfWorkCompletionException() + { + } + + public UnitOfWorkCompletionException( String string ) + { + super( string ); + } + + public UnitOfWorkCompletionException( String string, Throwable throwable ) + { + super( string, throwable ); + } + + public UnitOfWorkCompletionException( Throwable throwable ) + { + super( throwable ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkException.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkException.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkException.java new file mode 100644 index 0000000..4cc5c9f --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkException.java @@ -0,0 +1,45 @@ +/* Copyright 2007 Niclas Hedhman. + * + * 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; + +/** + * Base Exception for UnitOfWork related concerns. + */ +public class UnitOfWorkException + extends RuntimeException +{ + private static final long serialVersionUID = -8544178439804058558L; + + public UnitOfWorkException() + { + } + + public UnitOfWorkException( String message ) + { + super( message ); + } + + public UnitOfWorkException( String message, Throwable cause ) + { + super( message, cause ); + } + + public UnitOfWorkException( Throwable cause ) + { + super( cause ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkFactory.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkFactory.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkFactory.java new file mode 100644 index 0000000..0f3bcb4 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkFactory.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2007, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2007, Niclas Hedhman. 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 org.apache.zest.api.entity.EntityComposite; +import org.apache.zest.api.usecase.Usecase; + +/** + * Factory for UnitOfWork. + */ +public interface UnitOfWorkFactory +{ + /** + * Create a new UnitOfWork and associate it with the current thread. + * <p> + * The UnitOfWork will use the default Usecase settings. + * </p> + * <p> + * Current time will be set to System.currentTimeMillis(); + * </p> + * @return a new UnitOfWork + */ + UnitOfWork newUnitOfWork(); + + /** + * Create a new UnitOfWork and associate it with the current thread. + * <p> + * The UnitOfWork will use the default Usecase settings. + * </p> + * @return a new UnitOfWork + */ + UnitOfWork newUnitOfWork( long currentTime ); + + /** + * Create a new UnitOfWork for the given Usecase and associate it with the current thread. + * <p> + * Current time will be set to System.currentTimeMillis(); + * </p> + * @param usecase the Usecase for this UnitOfWork + * + * @return a new UnitOfWork + */ + UnitOfWork newUnitOfWork( Usecase usecase ); + + /** + * Create a new UnitOfWork for the given Usecase and associate it with the current thread. + * + * @param usecase the Usecase for this UnitOfWork + * + * @return a new UnitOfWork + */ + UnitOfWork newUnitOfWork( Usecase usecase, long currentTime ); + + /** + * @return true if there is an active UnitOfWork associated with the executing thread + */ + boolean isUnitOfWorkActive(); + + /** + * Returns the UnitOfWork that is currently associated with the executing thread. + * + * @return The current UnitOfWork associated with the executing thread + * + * @throws IllegalStateException if no current UnitOfWork is active + */ + UnitOfWork currentUnitOfWork() + throws IllegalStateException; + + /** + * Returns the UnitOfWork that the EntityComposite is bound to. + * + * @param entity the entity to be checked. + * + * @return The UnitOfWork instance that the Entity is bound to, or null if the entity is not associated with + * any UnitOfWork. + */ + UnitOfWork getUnitOfWork( EntityComposite entity ); +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkOptions.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkOptions.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkOptions.java new file mode 100644 index 0000000..176d9a0 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkOptions.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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; + +/** + * Set instances of this in MetaInfo on UnitOfWork or the associated Usecase. + * <p> + * Options: + * </p> + * <p> + * "pruneOnPause": if true, then clear out all instances that have been loaded in the UoW but not modified + * </p> + */ +public class UnitOfWorkOptions +{ + private boolean pruneOnPause = false; + + public UnitOfWorkOptions( boolean pruneOnPause ) + { + this.pruneOnPause = pruneOnPause; + } + + public boolean isPruneOnPause() + { + return pruneOnPause; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkTemplate.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkTemplate.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkTemplate.java new file mode 100644 index 0000000..cd91cb9 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/UnitOfWorkTemplate.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.zest.api.structure.Module; +import org.apache.zest.api.usecase.Usecase; + +/** + * UnitOfWork Template. + */ +public abstract class UnitOfWorkTemplate<RESULT, ThrowableType extends Throwable> +{ + private Usecase usecase = Usecase.DEFAULT; + private int retries = 10; + private boolean complete = true; + + protected UnitOfWorkTemplate() + { + } + + protected UnitOfWorkTemplate( int retries, boolean complete ) + { + this.retries = retries; + this.complete = complete; + } + + protected UnitOfWorkTemplate( Usecase usecase, int retries, boolean complete ) + { + this.usecase = usecase; + this.retries = retries; + this.complete = complete; + } + + protected abstract RESULT withUnitOfWork( UnitOfWork uow ) + throws ThrowableType; + + @SuppressWarnings( "unchecked" ) + public RESULT withModule( Module module ) + throws ThrowableType, UnitOfWorkCompletionException + { + int loop = 0; + ThrowableType ex = null; + do + { + UnitOfWork uow = module.newUnitOfWork( usecase ); + + try + { + RESULT result = withUnitOfWork( uow ); + if( complete ) + { + try + { + uow.complete(); + return result; + } + catch( ConcurrentEntityModificationException e ) + { + // Retry? + ex = (ThrowableType) e; + } + } + } + catch( Throwable e ) + { + ex = (ThrowableType) e; + } + finally + { + uow.discard(); + } + } + while( loop++ < retries ); + + throw ex; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkConcern.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkConcern.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkConcern.java new file mode 100644 index 0000000..9891b36 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkConcern.java @@ -0,0 +1,181 @@ +/* Copyright 2008 Edward Yakop. + * Copyright 2009 Niclas Hedhman. + * + * 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.concern; + +import java.lang.reflect.Method; +import org.apache.zest.api.common.AppliesTo; +import org.apache.zest.api.concern.GenericConcern; +import org.apache.zest.api.injection.scope.Invocation; +import org.apache.zest.api.injection.scope.Structure; +import org.apache.zest.api.structure.Module; +import org.apache.zest.api.unitofwork.ConcurrentEntityModificationException; +import org.apache.zest.api.unitofwork.UnitOfWork; +import org.apache.zest.api.usecase.Usecase; +import org.apache.zest.api.usecase.UsecaseBuilder; + +/** + * {@code UnitOfWorkConcern} manages the unit of work complete, discard and retry policy. + * + * @see UnitOfWorkPropagation + * @see UnitOfWorkDiscardOn + */ +@AppliesTo( UnitOfWorkPropagation.class ) +public class UnitOfWorkConcern + extends GenericConcern +{ + private static final Class<?>[] DEFAULT_DISCARD_CLASSES = new Class[]{ Throwable.class }; + + @Structure + Module module; + + @Invocation + UnitOfWorkPropagation propagation; + + /** + * Handles method with {@code UnitOfWorkPropagation} annotation. + * + * @param proxy The object. + * @param method The invoked method. + * @param args The method arguments. + * + * @return The returned value of method invocation. + * + * @throws Throwable Thrown if the method invocation throw exception. + */ + @Override + public Object invoke( Object proxy, Method method, Object[] args ) + throws Throwable + { + UnitOfWorkPropagation.Propagation propagationPolicy = propagation.value(); + if( propagationPolicy == UnitOfWorkPropagation.Propagation.REQUIRED ) + { + if( module.isUnitOfWorkActive() ) + { + return next.invoke( proxy, method, args ); + } + else + { + Usecase usecase = usecase(); + return invokeWithCommit( proxy, method, args, module.newUnitOfWork( usecase ) ); + } + } + else if( propagationPolicy == UnitOfWorkPropagation.Propagation.MANDATORY ) + { + if( !module.isUnitOfWorkActive() ) + { + throw new IllegalStateException( "UnitOfWork was required but there is no available unit of work." ); + } + } + else if( propagationPolicy == UnitOfWorkPropagation.Propagation.REQUIRES_NEW ) + { + Usecase usecase = usecase(); + return invokeWithCommit( proxy, method, args, module.newUnitOfWork( usecase ) ); + } + return next.invoke( proxy, method, args ); + } + + private Usecase usecase() + { + String usecaseName = propagation.usecase(); + Usecase usecase; + if( usecaseName == null ) + { + usecase = Usecase.DEFAULT; + } + else + { + usecase = UsecaseBuilder.newUsecase( usecaseName ); + } + return usecase; + } + + protected Object invokeWithCommit( Object proxy, Method method, Object[] args, UnitOfWork currentUnitOfWork ) + throws Throwable + { + try + { + UnitOfWorkRetry retryAnnot = method.getAnnotation( UnitOfWorkRetry.class ); + int maxTries = 0; + long delayFactor = 0; + long initialDelay = 0; + if( retryAnnot != null ) + { + maxTries = retryAnnot.retries(); + initialDelay = retryAnnot.initialDelay(); + delayFactor = retryAnnot.delayFactory(); + } + int retry = 0; + while( true ) + { + Object result = next.invoke( proxy, method, args ); + try + { + currentUnitOfWork.complete(); + return result; + } + catch( ConcurrentEntityModificationException e ) + { + if( retry >= maxTries ) + { + throw e; + } + module.currentUnitOfWork().discard(); + Thread.sleep( initialDelay + retry * delayFactor ); + retry++; + currentUnitOfWork = module.newUnitOfWork( usecase() ); + } + } + } + catch( Throwable throwable ) + { + // Discard only if this concern create a unit of work + discardIfRequired( method, currentUnitOfWork, throwable ); + throw throwable; + } + } + + /** + * Discard unit of work if the discard policy match. + * + * @param aMethod The invoked method. This argument must not be {@code null}. + * @param aUnitOfWork The current unit of work. This argument must not be {@code null}. + * @param aThrowable The exception thrown. This argument must not be {@code null}. + */ + protected void discardIfRequired( Method aMethod, UnitOfWork aUnitOfWork, Throwable aThrowable ) + { + UnitOfWorkDiscardOn discardPolicy = aMethod.getAnnotation( UnitOfWorkDiscardOn.class ); + Class<?>[] discardClasses; + if( discardPolicy != null ) + { + discardClasses = discardPolicy.value(); + } + else + { + discardClasses = DEFAULT_DISCARD_CLASSES; + } + + Class<? extends Throwable> aThrowableClass = aThrowable.getClass(); + for( Class<?> discardClass : discardClasses ) + { + if( discardClass.isAssignableFrom( aThrowableClass ) ) + { + aUnitOfWork.discard(); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkDiscardOn.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkDiscardOn.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkDiscardOn.java new file mode 100644 index 0000000..566ddef --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkDiscardOn.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2008, Edward Yakop. 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.concern; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotation to denote the unit of work discard policy. + * <p> + * By default, discard is applied on any method that has {@link UnitOfWorkPropagation} and any exception is thrown. + * </p> + * <p> + * Apply {@code UnitOfWorkDiscardOn} to override the default settings. + * </p> + * <p> + * Usage example: + * </p> + * <pre> + * <code> + * + * @Concerns( UnitOfWorkConcern.class ) + * public class MyBusinessServiceMixin implements BusinessService + * { + * @Structure UnitOfWorkFactory uowf; + * + * @UnitOfWorkDiscardOn( MyBusinessException.class ) + * public void myBusinessMethod() + * { + * // Must invoke current unit of work. + * UnitOfWork uow = uowf.currentUnitOfWork(); + * + * // Perform business logic + * } + * } + * </code> + * </pre> + * + * <p> + * The unit of work will be discarded iff {@code MyBusinessException} exceptions or its subclass is thrown from within + * {@code myBusinessMethod} method. + * </p> + */ +@Retention( RUNTIME ) +@Target( METHOD ) +@Inherited +@Documented +public @interface UnitOfWorkDiscardOn +{ + Class<? extends Throwable>[] value() default { Throwable.class }; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkPropagation.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkPropagation.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkPropagation.java new file mode 100644 index 0000000..3c0f23e --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkPropagation.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2008, Edward Yakop. All Rights Reserved. + * Copyright (c) 2009, Niclas Hedhman. 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.concern; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotation to denote the unit of work propagation. + * <p> + * Usage example: + * </p> + * <pre> + * <code> + * + * @Concerns( UnitOfWorkConcern.class ) + * public class MyBusinessServiceMixin implements BusinessService + * { + * @Structure UnitOfWorkFactory uowf; + * + * @UnitOfWorkPropagation + * public void myBusinessMethod() + * { + * // Must invoke current unit of work. + * UnitOfWork uow = uowf.currentUnitOfWork(); + * + * // Perform business logic + * } + * } + * </code> + * </pre> + */ +@Retention( RUNTIME ) +@Target( METHOD ) +@Inherited +@Documented +public @interface UnitOfWorkPropagation +{ + Propagation value() default Propagation.REQUIRED; + + String usecase() default ""; + + /** + * Propagation behaviors. + */ + enum Propagation + { + /** + * Default propagation behavior. + * Behavior: <br> + * If no current transaction: creates a new UnitOfWork <br> + * If there is a current UnitOfWork: use the current UnitOfWork. + */ + REQUIRED, + + /** + * Behavior: <br> + * If no current UnitOfWork: throw an exception <br> + * If there is a current UnitOfWork: use the current UnitOfWork. + */ + MANDATORY, + + /** + * Behavior: <br> + * If no current UnitOfWork: creates a new UnitOfWork <br> + * If there is a current UnitOfWork: suspend the current UnitOfWork and create a new UnitOfWork. + */ + REQUIRES_NEW + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkRetry.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkRetry.java b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkRetry.java new file mode 100644 index 0000000..490cc49 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/UnitOfWorkRetry.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.concern; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * This annotation describes the retries that should occur in case of {@link org.apache.zest.api.unitofwork.ConcurrentEntityModificationException} + * occurs. + */ +@Retention( RUNTIME ) +@Target( METHOD ) +@Inherited +@Documented +public @interface UnitOfWorkRetry +{ + int retries() default 1; + + long initialDelay() default 0; + + long delayFactory() default 10; +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/package.html b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/package.html new file mode 100644 index 0000000..68cbe0e --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/concern/package.html @@ -0,0 +1,24 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. +--> +<html> + <body> + <h2>UnitOfWork Concerns.</h2> + <p> + UnitOfWork Concerns allow declarative UnitOfWork propagation, discard wrt. exceptions and automatic retry. + </p> + </body> +</html> http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/unitofwork/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/unitofwork/package.html b/core/api/src/main/java/org/apache/zest/api/unitofwork/package.html new file mode 100644 index 0000000..1bc08fa --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/unitofwork/package.html @@ -0,0 +1,21 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. +--> +<html> + <body> + <h2>UnitOfWork API.</h2> + </body> +</html> http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/usecase/Usecase.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/usecase/Usecase.java b/core/api/src/main/java/org/apache/zest/api/usecase/Usecase.java new file mode 100644 index 0000000..f5bd66b --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/usecase/Usecase.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. 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.usecase; + +import java.io.Serializable; +import org.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.structure.MetaInfoHolder; + +/** + * A Usecase. A Usecase is used as a model for UnitOfWork, and helps + * implementations decide what to do in certain circumstances. + */ +public final class Usecase + implements Serializable, MetaInfoHolder +{ + public static final Usecase DEFAULT = new Usecase( "Default", new MetaInfo() ); + + private static final long serialVersionUID = 1L; + private final String name; + private final MetaInfo metaInfo; + + Usecase( String name, MetaInfo metaInfo ) + { + this.name = name; + this.metaInfo = metaInfo; + } + + /** + * Name of the usecase. + * + * @return the name + */ + public String name() + { + return name; + } + + /** + * Meta-info for the usecase. This can be of any type, and is typically set when creating the usecase + * and read during the execution of the usecase. + * + * @param infoType the MetaInfo type to retrieve. + * + * @return the previously stored metaInfo of the given type for the usecase. + */ + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + @Override + public String toString() + { + return name + ", meta info:" + metaInfo; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/usecase/UsecaseBuilder.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/usecase/UsecaseBuilder.java b/core/api/src/main/java/org/apache/zest/api/usecase/UsecaseBuilder.java new file mode 100644 index 0000000..3bbbd93 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/usecase/UsecaseBuilder.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. 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.usecase; + +import org.apache.zest.api.common.MetaInfo; + +/** + * Builder for Usecases. + */ +public final class UsecaseBuilder +{ + public static UsecaseBuilder buildUsecase( String aName ) + { + return new UsecaseBuilder( aName ); + } + + public static Usecase newUsecase( String aName ) + { + return new UsecaseBuilder( aName ).newUsecase(); + } + + private MetaInfo metaInfo = new MetaInfo(); + + private String name; + + private UsecaseBuilder( String name ) + { + this.name = name; + } + + public UsecaseBuilder withMetaInfo( Object metaInfo ) + { + this.metaInfo.set( metaInfo ); + return this; + } + + public Usecase newUsecase() + { + return new Usecase( name, metaInfo ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/usecase/package.html ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/usecase/package.html b/core/api/src/main/java/org/apache/zest/api/usecase/package.html new file mode 100644 index 0000000..71c696a --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/usecase/package.html @@ -0,0 +1,21 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. +--> +<html> + <body> + <h2>Usecase API.</h2> + </body> +</html> http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/util/Annotations.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/util/Annotations.java b/core/api/src/main/java/org/apache/zest/api/util/Annotations.java new file mode 100644 index 0000000..553e6c1 --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/util/Annotations.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. 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.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Type; +import org.apache.zest.functional.Function; +import org.apache.zest.functional.Iterables; +import org.apache.zest.functional.Specification; + +import static org.apache.zest.api.util.Classes.interfacesOf; +import static org.apache.zest.api.util.Classes.typeOf; +import static org.apache.zest.functional.Iterables.flatten; +import static org.apache.zest.functional.Iterables.flattenIterables; +import static org.apache.zest.functional.Iterables.iterable; +import static org.apache.zest.functional.Iterables.map; + +/** + * Useful methods for handling Annotations. + */ +public final class Annotations +{ + public static Function<Type, Iterable<Annotation>> ANNOTATIONS_OF = Classes.forTypes( new Function<Type, Iterable<Annotation>>() + { + @Override + public Iterable<Annotation> map( Type type ) + { + return Iterables.iterable( Classes.RAW_CLASS.map( type ).getAnnotations() ); + } + } ); + + public static Specification<AnnotatedElement> hasAnnotation( final Class<? extends Annotation> annotationType ) + { + return new Specification<AnnotatedElement>() + { + @Override + public boolean satisfiedBy( AnnotatedElement element ) + { + return element.getAnnotation( annotationType ) != null; + } + }; + } + + public static Function<Annotation, Class<? extends Annotation>> type() + { + return new Function<Annotation, Class<? extends Annotation>>() + { + @Override + public Class<? extends Annotation> map( Annotation annotation ) + { + return annotation.annotationType(); + } + }; + } + + public static Specification<Annotation> isType( final Class<? extends Annotation> annotationType ) + { + return new Specification<Annotation>() + { + @Override + public boolean satisfiedBy( Annotation annotation ) + { + return annotation.annotationType().equals( annotationType ); + } + }; + } + + public static <T extends Annotation> T annotationOn( Type type, Class<T> annotationType ) + { + return annotationType.cast( Classes.RAW_CLASS.map( type ).getAnnotation( annotationType ) ); + } + + public static Iterable<Annotation> findAccessorAndTypeAnnotationsIn( AccessibleObject accessor ) + { + return flatten( iterable( accessor.getAnnotations() ), + flattenIterables( map( Annotations.ANNOTATIONS_OF, interfacesOf( typeOf( accessor ) ) ) ) ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/061ddaa0/core/api/src/main/java/org/apache/zest/api/util/Base64Encoder.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/apache/zest/api/util/Base64Encoder.java b/core/api/src/main/java/org/apache/zest/api/util/Base64Encoder.java new file mode 100644 index 0000000..2779d6e --- /dev/null +++ b/core/api/src/main/java/org/apache/zest/api/util/Base64Encoder.java @@ -0,0 +1,224 @@ +/* + * Copyright 2009 Alin Dreghiciu. + * + * 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.util; + +/** + * Base64Encoder. + */ +public final class Base64Encoder +{ + + /** + * Utility class. ment to be used via static methods. + */ + private Base64Encoder() + { + // utility class + } + + /** + * Encodes a String into a base 64 String. The resulting encoding is chunked at 76 bytes. + * + * @param s String to encode. + * + * @return encoded string. + */ + public static String encode( String s, boolean includePadding ) + { + byte[] sBytes = s.getBytes(); + sBytes = encode( sBytes, includePadding ); + s = new String( sBytes ); + return s; + } + + /** + * Decodes a base 64 String into a String. + * + * @param s String to decode. + * + * @return encoded string. + * + * @throws java.lang.IllegalArgumentException + * _ If the given byte array was not valid base64 encoding. + */ + public static String decode( String s ) + throws IllegalArgumentException + { + s = s.replaceAll( "\n", "" ); + s = s.replaceAll( "\r", "" ); + byte[] sBytes = s.getBytes(); + sBytes = decode( sBytes ); + s = new String( sBytes ); + return s; + } + + private static final byte[] ALPHASET = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".getBytes(); + + private static final int I6O2 = 255 - 3; + private static final int O6I2 = 3; + private static final int I4O4 = 255 - 15; + private static final int O4I4 = 15; + private static final int I2O6 = 255 - 63; + private static final int O2I6 = 63; + + /** + * Encodes a byte array into a base 64 byte array. + * + * @param dData byte array to encode. + * + * @return encoded byte array. + */ + public static byte[] encode( byte[] dData, boolean includePadding ) + { + if( dData == null ) + { + throw new IllegalArgumentException( "Cannot encode null" ); + } + byte[] eData = new byte[ ( ( dData.length + 2 ) / 3 ) * 4 ]; + + int eIndex = 0; + for( int i = 0; i < dData.length; i += 3 ) + { + int d1; + int d2 = 0; + int d3 = 0; + int e1; + int e2; + int e3; + int e4; + int pad = 0; + + d1 = dData[ i ]; + if( ( i + 1 ) < dData.length ) + { + d2 = dData[ i + 1 ]; + if( ( i + 2 ) < dData.length ) + { + d3 = dData[ i + 2 ]; + } + else + { + pad = 1; + } + } + else + { + pad = 2; + } + + e1 = ALPHASET[ ( d1 & I6O2 ) >> 2 ]; + e2 = ALPHASET[ ( d1 & O6I2 ) << 4 | ( d2 & I4O4 ) >> 4 ]; + e3 = ALPHASET[ ( d2 & O4I4 ) << 2 | ( d3 & I2O6 ) >> 6 ]; + e4 = ALPHASET[ ( d3 & O2I6 ) ]; + + eData[ eIndex++ ] = (byte) e1; + eData[ eIndex++ ] = (byte) e2; + eData[ eIndex++ ] = ( pad < 2 ) ? (byte) e3 : (byte) '='; + eData[ eIndex++ ] = ( pad < 1 ) ? (byte) e4 : (byte) '='; + + if( pad > 0 && !includePadding ) + { + byte[] neweData = new byte[ eData.length - pad ]; + System.arraycopy( eData, 0, neweData, 0, eIndex - pad ); + eData = neweData; + } + } + + return eData; + } + + private final static int[] CODES = new int[ 256 ]; + + static + { + for( int i = 0; i < CODES.length; i++ ) + { + CODES[ i ] = 64; + } + for( int i = 0; i < ALPHASET.length; i++ ) + { + CODES[ ALPHASET[ i ] ] = i; + } + } + + /** + * Decodes a base64 byte array into a byte array. + * <p> + * + * @param eData byte array to decode. + * + * @return decoded byte array. + * + * @throws java.lang.IllegalArgumentException + * thrown if the given byte array was not valid com.sun.syndication.io.impl.Base64 encoding. + */ + public static byte[] decode( byte[] eData ) + { + if( eData == null ) + { + throw new IllegalArgumentException( "Cannot decode null" ); + } + byte[] cleanEData = eData.clone(); + int cleanELength = 0; + for( byte anEData : eData ) + { + if( anEData < 256 && CODES[ anEData ] < 64 ) + { + cleanEData[ cleanELength++ ] = anEData; + } + } + + int dLength = ( cleanELength / 4 ) * 3; + switch( cleanELength % 4 ) + { + case 3: + dLength += 2; + break; + case 2: + dLength++; + break; + } + + byte[] dData = new byte[ dLength ]; + int dIndex = 0; + for( int i = 0; i < eData.length; i += 4 ) + { + if( ( i + 3 ) > eData.length ) + { + throw new IllegalArgumentException( + "byte array is not a valid base64 encoding" + ); + } + int e1 = CODES[ cleanEData[ i ] ]; + int e2 = CODES[ cleanEData[ i + 1 ] ]; + int e3 = CODES[ cleanEData[ i + 2 ] ]; + int e4 = CODES[ cleanEData[ i + 3 ] ]; + dData[ dIndex++ ] = (byte) ( ( e1 << 2 ) | ( e2 >> 4 ) ); + if( dIndex < dData.length ) + { + dData[ dIndex++ ] = (byte) ( ( e2 << 4 ) | ( e3 >> 2 ) ); + } + if( dIndex < dData.length ) + { + dData[ dIndex++ ] = (byte) ( ( e3 << 6 ) | ( e4 ) ); + } + } + return dData; + } +}
