You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by ni...@apache.org on 2015/07/31 04:47:52 UTC
[33/51] [abbrv] [partial] zest-java git commit: Revert "First round
of changes to move to org.apache.zest namespace."
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/type/MatchTypeSpecification.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/type/MatchTypeSpecification.java b/core/api/src/main/java/org/qi4j/api/type/MatchTypeSpecification.java
new file mode 100644
index 0000000..ff7e8e5
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/type/MatchTypeSpecification.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012, 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.qi4j.api.type;
+
+import org.qi4j.functional.Specification;
+
+/**
+ * Match Type Specification for HasTypes.
+ */
+public class MatchTypeSpecification
+ implements Specification<HasTypes>
+{
+ private final Class<?> matchType;
+
+ public MatchTypeSpecification( Class<?> matchType )
+ {
+ this.matchType = matchType;
+ }
+
+ @Override
+ public boolean satisfiedBy( HasTypes item )
+ {
+ for( Class<?> type : item.types() )
+ {
+ if( matchType.isAssignableFrom( type ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/type/Serialization.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/type/Serialization.java b/core/api/src/main/java/org/qi4j/api/type/Serialization.java
new file mode 100644
index 0000000..57184a0
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/type/Serialization.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009, 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.qi4j.api.type;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Serialization options for Property intstances.
+ * <p>
+ * The {@code entry} type represents the explicit key=keyValue, value=valueValue. For JSON serialization;
+ * </p>
+ * <pre>
+ * [
+ * { "key1" : "value1" },
+ * { "key2" : "value2" }
+ * ]
+ * </pre>
+ * <p>
+ * For XML serialization;
+ * </p>
+ * <pre>
+ * <object>
+ * <
+ * </object>
+ * </pre>
+ * <p>
+ * The {@code object} type represents the explicit keyValue=valueValue.
+ * </p>
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( { ElementType.TYPE, ElementType.METHOD } )
+@Documented
+public @interface Serialization
+{
+ Variant value();
+
+ enum Variant
+ {
+ entry, object
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/type/ValueCompositeType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/type/ValueCompositeType.java b/core/api/src/main/java/org/qi4j/api/type/ValueCompositeType.java
new file mode 100644
index 0000000..c546625
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/type/ValueCompositeType.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2009, 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.qi4j.api.type;
+
+import java.lang.reflect.Type;
+import org.qi4j.api.association.AssociationDescriptor;
+import org.qi4j.api.property.PropertyDescriptor;
+import org.qi4j.api.util.Classes;
+import org.qi4j.api.value.ValueComposite;
+import org.qi4j.api.value.ValueDescriptor;
+
+/**
+ * ValueComposite ValueType.
+ */
+public final class ValueCompositeType
+ extends ValueType
+{
+ private final ValueDescriptor model;
+
+ public static boolean isValueComposite( Type type )
+ {
+ return ValueComposite.class.isAssignableFrom( Classes.RAW_CLASS.map( type ) );
+ }
+
+ public ValueCompositeType( ValueDescriptor model )
+ {
+ super( model.types() );
+ this.model = model;
+ }
+
+ public Iterable<? extends PropertyDescriptor> properties()
+ {
+ return model.state().properties();
+ }
+
+ public Iterable<? extends AssociationDescriptor> associations()
+ {
+ return model.state().associations();
+ }
+
+ public Iterable<? extends AssociationDescriptor> manyAssociations()
+ {
+ return model.state().manyAssociations();
+ }
+
+ public Iterable<? extends AssociationDescriptor> namedAssociations()
+ {
+ return model.state().namedAssociations();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/type/ValueType.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/type/ValueType.java b/core/api/src/main/java/org/qi4j/api/type/ValueType.java
new file mode 100644
index 0000000..ede914c
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/type/ValueType.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2009, Rickard Öberg. 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.api.type;
+
+import java.util.Collections;
+import org.qi4j.api.util.NullArgumentException;
+import org.qi4j.functional.Function;
+import org.qi4j.functional.Iterables;
+
+import static org.qi4j.functional.Iterables.first;
+
+/**
+ * Base class for types of values in ValueComposites and Properties.
+ */
+public class ValueType
+ implements HasTypes
+{
+
+ public static ValueType of( Class<?> type )
+ {
+ return new ValueType( type );
+ }
+
+ /**
+ * Check if a non-null object is of any of the Primitive Value Types or an array of them.
+ * <p>
+ * String, Boolean, Integer, Double, Float, Long, Byte, Short and Character and their Java primitive types
+ * counterparts are considered as Primitive Value Types.
+ * </p>
+ * <p>
+ * Date, BigInteger, BigDecimal and JodaTime types are not considered as Primitive Value Types.
+ * </p>
+ *
+ * @return true if object is a primitive value or an array of primitive values
+ * @throws IllegalArgumentException if object is null
+ */
+ public static boolean isPrimitiveValue( Object object )
+ {
+ NullArgumentException.validateNotNull( "object", object );
+ if( object instanceof String
+ || object instanceof Character
+ || object instanceof Boolean
+ || object instanceof Integer
+ || object instanceof Double
+ || object instanceof Float
+ || object instanceof Long
+ || object instanceof Byte
+ || object instanceof Short )
+ {
+ return true;
+ }
+ if( object.getClass().isArray() )
+ {
+ return isArrayOfPrimitiveValues( object );
+ }
+ return false;
+ }
+
+ private static boolean isArrayOfPrimitiveValues( Object array )
+ {
+ if( array instanceof String[]
+ || array instanceof char[] || array instanceof Character[]
+ || array instanceof boolean[] || array instanceof Boolean[]
+ || array instanceof int[] || array instanceof Integer[]
+ || array instanceof double[] || array instanceof Double[]
+ || array instanceof float[] || array instanceof Float[]
+ || array instanceof long[] || array instanceof Long[]
+ || array instanceof byte[] || array instanceof Byte[]
+ || array instanceof short[] || array instanceof Short[] )
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isPrimitiveValueType( ValueType valueType )
+ {
+ return isPrimitiveValueType( valueType.mainType() );
+ }
+
+ /**
+ * @see ValueType#isPrimitiveValue(java.lang.Object)
+ */
+ public static boolean isPrimitiveValueType( Class<?> type )
+ {
+ NullArgumentException.validateNotNull( "type", type );
+ if( String.class.isAssignableFrom( type ) )
+ {
+ return true;
+ }
+ if( type.isArray() )
+ {
+ return isPrimitiveValueType( type.getComponentType() );
+ }
+ return false;
+ }
+ protected final Iterable<Class<?>> types;
+
+ public ValueType( Class<?> type )
+ {
+ this( Collections.singleton( type ) );
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public ValueType( Iterable<? extends Class<?>> types )
+ {
+ this.types = (Iterable<Class<?>>) types;
+ }
+
+ public Class<?> mainType()
+ {
+ return first( types );
+ }
+
+ @Override
+ public Iterable<Class<?>> types()
+ {
+ return types;
+ }
+
+ @Override
+ public String toString()
+ {
+ String name = Iterables.toString(
+ types,
+ new Function<Class<?>, String>()
+ {
+ @Override
+ public String map( Class<?> item )
+ {
+ return item.getName();
+ }
+ },
+ "," );
+ if( name.contains( "," ) )
+ {
+ name = "{" + name + "}";
+ }
+ return name;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/type/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/type/package.html b/core/api/src/main/java/org/qi4j/api/type/package.html
new file mode 100644
index 0000000..d42baa3
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/type/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>Type API.</h2>
+ </body>
+</html>
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/ConcurrentEntityModificationException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/ConcurrentEntityModificationException.java b/core/api/src/main/java/org/qi4j/api/unitofwork/ConcurrentEntityModificationException.java
new file mode 100644
index 0000000..7745b5a
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/unitofwork/ConcurrentEntityModificationException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.qi4j.api.unitofwork;
+
+import org.qi4j.api.entity.EntityComposite;
+
+/**
+ * This exception is thrown by UnitOfWork.complete() if any entities that are being committed
+ * had been changed while the UnitOfWork was being executed.
+ */
+public class ConcurrentEntityModificationException
+ extends UnitOfWorkCompletionException
+{
+ private static final long serialVersionUID = 3872723845064767689L;
+
+ private final Iterable<EntityComposite> concurrentlyModifiedEntities;
+
+ public ConcurrentEntityModificationException( Iterable<EntityComposite> concurrentlyModifiedEntities )
+ {
+ super("Entities changed concurrently :" + concurrentlyModifiedEntities);
+ this.concurrentlyModifiedEntities = concurrentlyModifiedEntities;
+ }
+
+ public Iterable<EntityComposite> concurrentlyModifiedEntities()
+ {
+ return concurrentlyModifiedEntities;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/EntityCompositeAlreadyExistsException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/EntityCompositeAlreadyExistsException.java b/core/api/src/main/java/org/qi4j/api/unitofwork/EntityCompositeAlreadyExistsException.java
new file mode 100644
index 0000000..66e388d
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/unitofwork/EntityCompositeAlreadyExistsException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008 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.qi4j.api.unitofwork;
+
+import org.qi4j.api.entity.EntityReference;
+
+/**
+ * If you try to create an EntityComposite whose identity already exists,
+ * then this exception will be thrown.
+ */
+public class EntityCompositeAlreadyExistsException
+ extends UnitOfWorkException
+{
+ private static final long serialVersionUID = -7297710939536508481L;
+
+ private final EntityReference identity;
+
+ public EntityCompositeAlreadyExistsException( EntityReference identity )
+ {
+ super( "EntityComposite (" + identity + ") already exists." );
+ this.identity = identity;
+ }
+
+ public EntityReference identity()
+ {
+ return identity;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/EntityTypeNotFoundException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/EntityTypeNotFoundException.java b/core/api/src/main/java/org/qi4j/api/unitofwork/EntityTypeNotFoundException.java
new file mode 100644
index 0000000..39765fd
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/unitofwork/EntityTypeNotFoundException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2007, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2007-2008, Niclas Hedhman. All Rights Reserved.
+ * Copyright (c) 2007, Alin Dreghiciu. 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.api.unitofwork;
+
+import org.qi4j.functional.Function;
+
+import static org.qi4j.functional.Iterables.fold;
+
+/**
+ * Zest exception to be thrown in case that an entity composite
+ * was not found during a lookup call.
+ */
+public class EntityTypeNotFoundException
+ extends UnitOfWorkException
+{
+ private final String compositeType;
+
+ public EntityTypeNotFoundException( String entityType, String moduleName, Iterable<String> visibility )
+ {
+ super( "Could not find an EntityComposite of type " + entityType + " in module [" + moduleName + "].\n" +
+ "\tThe following entity types are visible:\n" + join(visibility) );
+ this.compositeType = entityType;
+ }
+
+ private static String join( Iterable<String> visibility )
+ {
+ return fold( new Function<String, String>()
+ {
+ StringBuilder result;
+ {
+ result = new StringBuilder();
+ }
+
+ @Override
+ public String map( String type )
+ {
+ result.append( type );
+ result.append( "\n" );
+ return result.toString();
+ }
+ }, visibility );
+ }
+
+ public String compositeType()
+ {
+ return compositeType;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/NoSuchEntityException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/NoSuchEntityException.java b/core/api/src/main/java/org/qi4j/api/unitofwork/NoSuchEntityException.java
new file mode 100644
index 0000000..cbffe9d
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/unitofwork/NoSuchEntityException.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2008-2013, 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.qi4j.api.unitofwork;
+
+import org.qi4j.api.entity.EntityReference;
+import org.qi4j.api.usecase.Usecase;
+import org.qi4j.functional.Function;
+import org.qi4j.functional.Iterables;
+
+/**
+ * This exception indicates that the requested Entity with the given
+ * identity does not exist.
+ */
+public class NoSuchEntityException
+ extends UnitOfWorkException
+{
+ private final EntityReference identity;
+ private final Usecase usecase;
+ private final Class<?>[] mixinTypes;
+
+ public NoSuchEntityException( EntityReference identity, Class<?> mixinType, Usecase usecase )
+ {
+ super( "Could not find entity (" + identity + ") of type " + mixinType.getName() + " in usecase '" + usecase.name() + "'" );
+ this.identity = identity;
+ this.usecase = usecase;
+ this.mixinTypes = new Class<?>[]{ mixinType };
+ }
+
+ public NoSuchEntityException( EntityReference identity, Class<?>[] mixinTypes, Usecase usecase )
+ {
+ super( "Could not find entity (" + identity + ") of type " + toString( mixinTypes ) + " in usecase '" + usecase.name() + "'" );
+ this.identity = identity;
+ this.mixinTypes = mixinTypes;
+ this.usecase = usecase;
+ }
+
+ public NoSuchEntityException( EntityReference identity, Iterable<Class<?>> types, Usecase usecase )
+ {
+ this( identity, castToArray( types ), usecase );
+ }
+
+ public EntityReference identity()
+ {
+ return identity;
+ }
+
+ public Class<?>[] mixinTypes()
+ {
+ return mixinTypes;
+ }
+
+ public Usecase usecase()
+ {
+ return usecase;
+ }
+
+ private static Class<?>[] castToArray( Iterable<Class<?>> iterableClasses )
+ {
+ Iterable<Class> types = Iterables.cast( iterableClasses );
+ return Iterables.toArray( Class.class, types );
+ }
+
+ private static String toString( Class<?>[] mixinTypes )
+ {
+ Iterable<String> map = Iterables.map( new Function<Class<?>, String>()
+ {
+ @Override
+ public String map( Class<?> item )
+ {
+ return item.getName();
+ }
+ }, Iterables.iterable( mixinTypes ) );
+ return Iterables.fold( new Function<String, String>()
+ {
+ StringBuilder result;
+ boolean first = true;
+
+ {
+ result = new StringBuilder();
+ result.append( "[" );
+ }
+
+ @Override
+ public String map( String strings )
+ {
+ if( !first )
+ {
+ result.append( ',' );
+ }
+ first = false;
+ result.append( strings );
+ return result.toString() + "]";
+ }
+ }, map );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWork.java
new file mode 100644
index 0000000..2ce6855
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.api.unitofwork;
+
+import java.util.Map;
+import org.qi4j.api.association.AssociationDescriptor;
+import org.qi4j.api.composite.AmbiguousTypeException;
+import org.qi4j.api.entity.EntityBuilder;
+import org.qi4j.api.entity.EntityReference;
+import org.qi4j.api.entity.Identity;
+import org.qi4j.api.entity.LifecycleException;
+import org.qi4j.api.property.PropertyDescriptor;
+import org.qi4j.api.query.Query;
+import org.qi4j.api.query.QueryBuilder;
+import org.qi4j.api.structure.MetaInfoHolder;
+import org.qi4j.api.usecase.Usecase;
+import org.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkCallback.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkCallback.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkCallback.java
new file mode 100644
index 0000000..77a78e9
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkCompletionException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkCompletionException.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkCompletionException.java
new file mode 100644
index 0000000..54f6c72
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkException.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkException.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkException.java
new file mode 100644
index 0000000..03b15d7
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkFactory.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkFactory.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkFactory.java
new file mode 100644
index 0000000..a8755d4
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.api.unitofwork;
+
+import org.qi4j.api.entity.EntityComposite;
+import org.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkOptions.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkOptions.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkOptions.java
new file mode 100644
index 0000000..33e544d
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkTemplate.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkTemplate.java b/core/api/src/main/java/org/qi4j/api/unitofwork/UnitOfWorkTemplate.java
new file mode 100644
index 0000000..e8663b4
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.api.unitofwork;
+
+import org.qi4j.api.structure.Module;
+import org.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkConcern.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkConcern.java b/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkConcern.java
new file mode 100644
index 0000000..c684385
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkConcern.java
@@ -0,0 +1,182 @@
+/* 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.qi4j.api.unitofwork.concern;
+
+import java.lang.reflect.Method;
+import org.qi4j.api.common.AppliesTo;
+import org.qi4j.api.concern.GenericConcern;
+import org.qi4j.api.injection.scope.Invocation;
+import org.qi4j.api.injection.scope.Structure;
+import org.qi4j.api.injection.scope.Uses;
+import org.qi4j.api.structure.Module;
+import org.qi4j.api.unitofwork.ConcurrentEntityModificationException;
+import org.qi4j.api.unitofwork.UnitOfWork;
+import org.qi4j.api.usecase.Usecase;
+import org.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkDiscardOn.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkDiscardOn.java b/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkDiscardOn.java
new file mode 100644
index 0000000..3668736
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkPropagation.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkPropagation.java b/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkPropagation.java
new file mode 100644
index 0000000..4fe78b7
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkRetry.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkRetry.java b/core/api/src/main/java/org/qi4j/api/unitofwork/concern/UnitOfWorkRetry.java
new file mode 100644
index 0000000..de386f9
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.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.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/concern/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/concern/package.html b/core/api/src/main/java/org/qi4j/api/unitofwork/concern/package.html
new file mode 100644
index 0000000..68cbe0e
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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/a789141d/core/api/src/main/java/org/qi4j/api/unitofwork/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/unitofwork/package.html b/core/api/src/main/java/org/qi4j/api/unitofwork/package.html
new file mode 100644
index 0000000..1bc08fa
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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/a789141d/core/api/src/main/java/org/qi4j/api/usecase/Usecase.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/usecase/Usecase.java b/core/api/src/main/java/org/qi4j/api/usecase/Usecase.java
new file mode 100644
index 0000000..415672a
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.api.usecase;
+
+import java.io.Serializable;
+import org.qi4j.api.common.MetaInfo;
+import org.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/usecase/UsecaseBuilder.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/usecase/UsecaseBuilder.java b/core/api/src/main/java/org/qi4j/api/usecase/UsecaseBuilder.java
new file mode 100644
index 0000000..118b72d
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.api.usecase;
+
+import org.qi4j.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/a789141d/core/api/src/main/java/org/qi4j/api/usecase/package.html
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/usecase/package.html b/core/api/src/main/java/org/qi4j/api/usecase/package.html
new file mode 100644
index 0000000..71c696a
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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/a789141d/core/api/src/main/java/org/qi4j/api/util/Annotations.java
----------------------------------------------------------------------
diff --git a/core/api/src/main/java/org/qi4j/api/util/Annotations.java b/core/api/src/main/java/org/qi4j/api/util/Annotations.java
new file mode 100644
index 0000000..8adc5ff
--- /dev/null
+++ b/core/api/src/main/java/org/qi4j/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.qi4j.api.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+import org.qi4j.functional.Function;
+import org.qi4j.functional.Iterables;
+import org.qi4j.functional.Specification;
+
+import static org.qi4j.api.util.Classes.interfacesOf;
+import static org.qi4j.api.util.Classes.typeOf;
+import static org.qi4j.functional.Iterables.flatten;
+import static org.qi4j.functional.Iterables.flattenIterables;
+import static org.qi4j.functional.Iterables.iterable;
+import static org.qi4j.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 ) ) ) ) );
+ }
+}