polygene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nic...@apache.org
Subject [07/80] [partial] zest-java git commit: First round of changes to move to org.apache.zest namespace.
Date Thu, 30 Jul 2015 19:48:06 GMT
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java
new file mode 100644
index 0000000..0ebfbbb
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/TypeLookup.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2008-2012, Rickard Öberg.
+ * Copyright (c) 2008-2012, Niclas Hedhman.
+ * Copyright (c) 2012, Paul Merlin.
+ *
+ * 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.runtime.structure;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.zest.api.composite.AmbiguousTypeException;
+import org.apache.zest.api.composite.ModelDescriptor;
+import org.apache.zest.api.service.NoSuchServiceException;
+import org.apache.zest.api.service.ServiceReference;
+import org.apache.zest.functional.Function;
+import org.apache.zest.functional.Specification;
+import org.apache.zest.functional.Specifications;
+import org.apache.zest.runtime.composite.TransientModel;
+import org.apache.zest.runtime.entity.EntityModel;
+import org.apache.zest.runtime.object.ObjectModel;
+import org.apache.zest.runtime.value.ValueModel;
+import org.apache.zest.spi.module.ModelModule;
+
+import static org.apache.zest.api.common.Visibility.application;
+import static org.apache.zest.api.common.Visibility.layer;
+import static org.apache.zest.api.common.Visibility.module;
+import static org.apache.zest.api.util.Classes.RAW_CLASS;
+import static org.apache.zest.api.util.Classes.interfacesOf;
+import static org.apache.zest.functional.Iterables.cast;
+import static org.apache.zest.functional.Iterables.filter;
+import static org.apache.zest.functional.Iterables.first;
+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.toList;
+import static org.apache.zest.functional.Iterables.unique;
+
+/**
+ * Central place for Composite Type lookups.
+ */
+public class TypeLookup
+{
+
+    // Constructor parameters
+    private final ModuleInstance moduleInstance;
+    // Eager instance objects
+    private final Map<Class<?>, ModelModule<ObjectModel>> objectModels;
+    private final Map<Class<?>, ModelModule<TransientModel>> transientModels;
+    private final Map<Class<?>, ModelModule<ValueModel>> valueModels;
+    private final Map<Class<?>, Iterable<ModelModule<EntityModel>>> allEntityModels;
+    private final Map<Class<?>, ModelModule<EntityModel>> unambiguousEntityModels;
+    private final Map<Type, ServiceReference<?>> serviceReferences;
+    private final Map<Type, Iterable<ServiceReference<?>>> servicesReferences;
+
+    /**
+     * Create a new TypeLookup bound to the given ModuleInstance.
+     *
+     * @param moduleInstance ModuleInstance bound to this TypeLookup
+     */
+    /* package */ TypeLookup( ModuleInstance moduleInstance )
+    {
+        // Constructor parameters
+        this.moduleInstance = moduleInstance;
+
+        // Eager instance objects
+        objectModels = new ConcurrentHashMap<>();
+        transientModels = new ConcurrentHashMap<>();
+        valueModels = new ConcurrentHashMap<>();
+        allEntityModels = new ConcurrentHashMap<>();
+        unambiguousEntityModels = new ConcurrentHashMap<>();
+        serviceReferences = new ConcurrentHashMap<>();
+        servicesReferences = new ConcurrentHashMap<>();
+    }
+
+    /**
+     * Lookup first Object Model matching the given Type.
+     *
+     * <p>First, if Object Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Second, if Object Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Type lookup is done lazily and cached.</p>
+     *
+     * @param type Looked up Type
+     *
+     * @return First matching Object Model
+     */
+    @SuppressWarnings( { "raw", "unchecked" } )
+    /* package */ ModelModule<ObjectModel> lookupObjectModel( final Class type )
+    {
+        ModelModule<ObjectModel> model = objectModels.get( type );
+
+        if( model == null )
+        {
+            // Unambiguously and lazily resolve ObjectModel
+            Iterable<ModelModule<ObjectModel>> flatten = flatten(
+                ambiguousTypeCheck( type,
+                                    findModels( new ExactTypeLookupSpecification( type ),
+                                                moduleInstance.visibleObjects( module ),
+                                                moduleInstance.layerInstance().visibleObjects( layer ),
+                                                moduleInstance.layerInstance().visibleObjects( application ),
+                                                moduleInstance.layerInstance()
+                                                    .usedLayersInstance()
+                                                    .visibleObjects() ) ),
+                ambiguousTypeCheck( type,
+                                    findModels( new AssignableTypeLookupSpecification( type ),
+                                                moduleInstance.visibleObjects( module ),
+                                                moduleInstance.layerInstance().visibleObjects( layer ),
+                                                moduleInstance.layerInstance().visibleObjects( application ),
+                                                moduleInstance.layerInstance()
+                                                    .usedLayersInstance()
+                                                    .visibleObjects() ) ) );
+
+            model = first( flatten );
+
+            if( model != null )
+            {
+                objectModels.put( type, model );
+            }
+        }
+
+        return model;
+    }
+
+    /**
+     * Lookup first Transient Model matching the given Type.
+     *
+     * <p>First, if Transient Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Second, if Transient Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Type lookup is done lazily and cached.</p>
+     *
+     * @param type Looked up Type
+     *
+     * @return First matching Transient Model
+     */
+    @SuppressWarnings( { "raw", "unchecked" } )
+    /* package */ ModelModule<TransientModel> lookupTransientModel( final Class type )
+    {
+        ModelModule<TransientModel> model = transientModels.get( type );
+
+        if( model == null )
+        {
+            // Unambiguously and lazily resolve TransientModel
+            Iterable<ModelModule<TransientModel>> allModels = flatten(
+                ambiguousTypeCheck( type,
+                                    findModels( new ExactTypeLookupSpecification( type ),
+                                                moduleInstance.visibleTransients( module ),
+                                                moduleInstance.layerInstance().visibleTransients( layer ),
+                                                moduleInstance.layerInstance().visibleTransients( application ),
+                                                moduleInstance.layerInstance().usedLayersInstance().visibleTransients()
+                                    )
+                ),
+
+                ambiguousTypeCheck( type,
+                                    findModels( new AssignableTypeLookupSpecification( type ),
+                                                moduleInstance.visibleTransients( module ),
+                                                moduleInstance.layerInstance().visibleTransients( layer ),
+                                                moduleInstance.layerInstance().visibleTransients( application ),
+                                                moduleInstance.layerInstance().usedLayersInstance().visibleTransients()
+                                    )
+                )
+            );
+            model = first( allModels );
+
+            if( model != null )
+            {
+                transientModels.put( type, model );
+            }
+        }
+
+        return model;
+    }
+
+    /**
+     * Lookup first Value Model matching the given Type.
+     *
+     * <p>First, if Value Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Second, if Value Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Type lookup is done lazily and cached.</p>
+     *
+     * @param type Looked up Type
+     *
+     * @return First matching Value Model
+     */
+    @SuppressWarnings( { "raw", "unchecked" } )
+    public ModelModule<ValueModel> lookupValueModel( final Class type )
+    {
+        ModelModule<ValueModel> model = valueModels.get( type );
+
+        if( model == null )
+        {
+            // Unambiguously and lazily resolve ValueModel
+            Iterable<ModelModule<ValueModel>> flatten = flatten(
+                ambiguousTypeCheck( type,
+                                    findModels( new ExactTypeLookupSpecification( type ),
+                                                moduleInstance.visibleValues( module ),
+                                                moduleInstance.layerInstance().visibleValues( layer ),
+                                                moduleInstance.layerInstance().visibleValues( application ),
+                                                moduleInstance.layerInstance().usedLayersInstance().visibleValues() ) ),
+                ambiguousTypeCheck( type,
+                                    findModels( new AssignableTypeLookupSpecification( type ),
+                                                moduleInstance.visibleValues( module ),
+                                                moduleInstance.layerInstance().visibleValues( layer ),
+                                                moduleInstance.layerInstance().visibleValues( application ),
+                                                moduleInstance.layerInstance().usedLayersInstance().visibleValues()
+                                    )
+                )
+            );
+
+            model = first( flatten );
+
+            if( model != null )
+            {
+                valueModels.put( type, model );
+            }
+        }
+
+        return model;
+    }
+
+    /**
+     * Lookup first Entity Model matching the given Type.
+     *
+     * <p>First, if Entity Models exactly match the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Second, if Entity Models match a type assignable to the given type, the closest one (Visibility then Assembly order) is returned.
+     * Multiple <b>assignable</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     *
+     * <p>Type lookup is done lazily and cached.</p>
+     *
+     * <p><b>Should be used for creational use cases only.</b> For non-creational use cases see
+     * {@link #lookupEntityModels(java.lang.Class)}.</p>
+     *
+     * @param type Looked up Type
+     *
+     * @return First matching Entity Model
+     */
+    @SuppressWarnings( { "raw", "unchecked" } )
+    /* package */ ModelModule<EntityModel> lookupEntityModel( final Class type )
+    {
+        ModelModule<EntityModel> model = unambiguousEntityModels.get( type );
+
+        if( model == null )
+        {
+            // Unambiguously and lazily resolve EntityModels
+            Iterable<ModelModule<EntityModel>> allModels = flatten(
+                ambiguousTypeCheck( type,
+                                    findModels( new ExactTypeLookupSpecification( type ),
+                                                moduleInstance.visibleEntities( module ),
+                                                moduleInstance.layerInstance().visibleEntities( layer ),
+                                                moduleInstance.layerInstance().visibleEntities( application ),
+                                                moduleInstance.layerInstance()
+                                                    .usedLayersInstance()
+                                                    .visibleEntities() ) ),
+                ambiguousTypeCheck( type,
+                                    findModels( new AssignableTypeLookupSpecification( type ),
+                                                moduleInstance.visibleEntities( module ),
+                                                moduleInstance.layerInstance().visibleEntities( layer ),
+                                                moduleInstance.layerInstance().visibleEntities( application ),
+                                                moduleInstance.layerInstance().usedLayersInstance().visibleEntities()
+                                    )
+                )
+            );
+
+            model = first( allModels );
+
+            if( model != null )
+            {
+                unambiguousEntityModels.put( type, model );
+            }
+        }
+
+        return model;
+    }
+
+    /**
+     * Lookup all Entity Models matching the given Type.
+     *
+     * <p>Returned Iterable contains, in order, Entity Models that: </p>
+     *
+     * <ul>
+     * <li>exactly match the given type, in Visibility then Assembly order ;</li>
+     * <li>match a type assignable to the given type, in Visibility then Assembly order.</li>
+     * </ul>
+     *
+     * <p>Multiple <b>exact</b> matches with the same Visibility are <b>forbidden</b> and result in an AmbiguousTypeException.</p>
+     * <p>Multiple <b>assignable</b> matches are <b>allowed</b> to enable polymorphic fetches and queries.</p>
+     *
+     * <p>Type lookup is done lazily and cached.</p>
+     *
+     * <p><b>Should be used for non-creational use cases only.</b> For creational use cases see
+     * {@link #lookupEntityModel(java.lang.Class)}.</p>
+     *
+     * @param type Looked up Type
+     *
+     * @return All matching Entity Models
+     */
+    @SuppressWarnings( { "raw", "unchecked" } )
+    /* package */ Iterable<ModelModule<EntityModel>> lookupEntityModels( final Class type )
+    {
+        Iterable<ModelModule<EntityModel>> models = allEntityModels.get( type );
+        if( models == null )
+        {
+            // Ambiguously and lasily resolve EntityModels
+            Iterable<ModelModule<EntityModel>> matchingEntityModels = flatten(
+                ambiguousTypeCheck( type,
+                                    findModels( new ExactTypeLookupSpecification( type ),
+                                                moduleInstance.visibleEntities( module ),
+                                                moduleInstance.layerInstance().visibleEntities( layer ),
+                                                moduleInstance.layerInstance().visibleEntities( application ),
+                                                moduleInstance.layerInstance().usedLayersInstance().visibleEntities()
+                                    )
+                ),
+                findModels( new AssignableTypeLookupSpecification( type ),
+                            moduleInstance.visibleEntities( module ),
+                            moduleInstance.layerInstance().visibleEntities( layer ),
+                            moduleInstance.layerInstance().visibleEntities( application ),
+                            moduleInstance.layerInstance().usedLayersInstance().visibleEntities() ) );
+
+            // Don't return the same EntityModel multiple times
+            matchingEntityModels = unique( matchingEntityModels );
+
+            models = toList( matchingEntityModels );
+            allEntityModels.put( type, models );
+        }
+        return models;
+    }
+
+    /**
+     * Lookup first ServiceReference matching the given Type.
+     *
+     * <p>Type lookup is done lazily and cached.</p>
+     *
+     * <p>See {@link #lookupServiceReferences(java.lang.reflect.Type)}.</p>
+     *
+     * @param <T>         Service Type
+     * @param serviceType Looked up Type
+     *
+     * @return First matching ServiceReference
+     */
+    /* package */
+    @SuppressWarnings( "unchecked" )
+    <T> ServiceReference<T> lookupServiceReference( Type serviceType )
+    {
+        ServiceReference<?> serviceReference = serviceReferences.get( serviceType );
+        if( serviceReference == null )
+        {
+            // Lazily resolve ServiceReference
+            serviceReference = first( lookupServiceReferences( serviceType ) );
+            if( serviceReference != null )
+            {
+                serviceReferences.put( serviceType, serviceReference );
+            }
+        }
+
+        if( serviceReference == null )
+        {
+            throw new NoSuchServiceException( RAW_CLASS.map( serviceType ).getName(), moduleInstance.name() );
+        }
+
+        return (ServiceReference<T>) serviceReference;
+    }
+
+    /**
+     * Lookup all ServiceReferences matching the given Type.
+     *
+     * <p>Returned Iterable contains, in order, ServiceReferences that: </p>
+     *
+     * <ul>
+     * <li>exactly match the given type, in Visibility then Assembly order ;</li>
+     * <li>match a type assignable to the given type, in Visibility then Assembly order.</li>
+     * </ul>
+     *
+     * <p>Multiple <b>exact</b> matches with the same Visibility are <b>allowed</b> to enable polymorphic lookup/injection.</p>
+     * <p>Multiple <b>assignable</b> matches with the same Visibility are <b>allowed</b> for the very same reason.</p>
+     *
+     * <p>Type lookup is done lazily and cached.</p>
+     *
+     * @param <T>         Service Type
+     * @param serviceType Looked up Type
+     *
+     * @return All matching ServiceReferences
+     */
+    @SuppressWarnings( "unchecked" )
+    /* package */ <T> Iterable<ServiceReference<T>> lookupServiceReferences( final Type serviceType )
+    {
+        Iterable<ServiceReference<?>> serviceRefs = servicesReferences.get( serviceType );
+        if( serviceRefs == null )
+        {
+            // Lazily resolve ServicesReferences
+            Iterable<ServiceReference<?>> matchingServices = flatten(
+                findServiceReferences( new ExactTypeLookupSpecification( serviceType ),
+                                       moduleInstance.visibleServices( module ),
+                                       moduleInstance.layerInstance().visibleServices( layer ),
+                                       moduleInstance.layerInstance().visibleServices( application ),
+                                       moduleInstance.layerInstance().usedLayersInstance().visibleServices() ),
+                findServiceReferences( new AssignableTypeLookupSpecification( serviceType ),
+                                       moduleInstance.visibleServices( module ),
+                                       moduleInstance.layerInstance().visibleServices( layer ),
+                                       moduleInstance.layerInstance().visibleServices( application ),
+                                       moduleInstance.layerInstance().usedLayersInstance().visibleServices() ) );
+
+            // Don't return the same ServiceReference multiple times
+            matchingServices = unique( matchingServices );
+
+            serviceRefs = toList( matchingServices );
+            servicesReferences.put( serviceType, serviceRefs );
+        }
+
+        return cast( serviceRefs );
+    }
+
+    @SuppressWarnings( { "raw", "unchecked" } )
+    private static <T extends ModelDescriptor> Iterable<ModelModule<T>> findModels( Specification<Iterable<Class<?>>> specification,
+                                                                                    Iterable<ModelModule<T>>... models
+    )
+    {
+        Specification<ModelModule<T>> spec = Specifications.translate( new ModelModuleTypesFunction(), specification );
+        Iterable<ModelModule<T>> flattened = flattenIterables( iterable( models ) );
+        return filter( spec, flattened );
+    }
+
+    @SuppressWarnings( { "raw", "unchecked" } )
+    private static Iterable<ServiceReference<?>> findServiceReferences( Specification<Iterable<Class<?>>> specification,
+                                                                        Iterable<ServiceReference<?>>... references
+    )
+    {
+        Specification<ServiceReference<?>> spec = Specifications.translate( new ServiceReferenceTypesFunction(), specification );
+        Iterable<ServiceReference<?>> flattened = flattenIterables( iterable( references ) );
+        return filter( spec, flattened );
+    }
+
+    /**
+     * Check if the list of models contains several ones with the same visibility. If yes, then
+     * throw an AmbiguousTypeException
+     */
+    @SuppressWarnings( "raw" )
+    private static <T extends ModelDescriptor> Iterable<ModelModule<T>> ambiguousTypeCheck( final Class type,
+                                                                                            final Iterable<ModelModule<T>> models
+    )
+    {
+        return new Iterable<ModelModule<T>>()
+        {
+
+            @Override
+            public Iterator<ModelModule<T>> iterator()
+            {
+                ModelModule<T> current = null;
+                List<ModelModule<T>> ambiguous = null;
+                List<ModelModule<T>> results = new ArrayList<>();
+                for( ModelModule<T> model : models )
+                {
+                    if( current != null && !model.equals( current ) )
+                    {
+                        if( model.model().visibility() == current.model().visibility() )
+                        {
+                            if( ambiguous == null )
+                            {
+                                ambiguous = new ArrayList<>();
+                            }
+                            ambiguous.add( model );
+                        }
+                    }
+                    else
+                    {
+                        current = model;
+                    }
+                    results.add( model );
+                }
+                if( ambiguous != null )
+                {
+                    // Check if we had any ambiguities
+                    ambiguous.add( current );
+                    throw new AmbiguousTypeException( "More than one type matches " + type.getName() + ":" + ambiguous );
+                }
+                // Ambiguity check done, and no ambiguities found. Return results
+                return results.iterator();
+            }
+        };
+    }
+
+    private static class ModelModuleTypesFunction<T extends ModelDescriptor>
+        implements Function<ModelModule<T>, Iterable<Class<?>>>
+    {
+
+        @Override
+        public Iterable<Class<?>> map( ModelModule<T> modelModule )
+        {
+            return modelModule.model().types();
+        }
+    }
+
+    private static class ServiceReferenceTypesFunction
+        implements Function<ServiceReference<?>, Iterable<Class<?>>>
+    {
+
+        @Override
+        public Iterable<Class<?>> map( ServiceReference<?> serviceReference )
+        {
+            return serviceReference.types();
+        }
+    }
+
+    private static abstract class AbstractTypeLookupSpecification
+        implements Specification<Iterable<Class<?>>>
+    {
+
+        protected final Type lookedUpType;
+
+        private AbstractTypeLookupSpecification( Type lookedUpType )
+        {
+            this.lookedUpType = lookedUpType;
+        }
+
+        @Override
+        public final boolean satisfiedBy( Iterable<Class<?>> types )
+        {
+            if( lookedUpType instanceof Class )
+            {
+                // Straight class assignability check
+                return checkClassMatch( types, (Class) lookedUpType );
+            }
+            else
+            {
+                if( lookedUpType instanceof ParameterizedType )
+                {
+                    // Foo<Bar> check
+                    // First check Foo
+                    ParameterizedType parameterizedType = (ParameterizedType) lookedUpType;
+                    if( !checkClassMatch( types, (Class) parameterizedType.getRawType() ) )
+                    {
+                        return false;
+                    }
+                    // Then check Bar
+                    for( Type intf : interfacesOf( types ) )
+                    {
+                        if( intf.equals( lookedUpType ) )
+                        {
+                            // All parameters are the same - ok!
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+                else if( lookedUpType instanceof WildcardType )
+                {
+                    return true;
+                }
+                return false;
+            }
+        }
+
+        private boolean checkClassMatch( Iterable<Class<?>> candidates, Class<?> lookedUpType )
+        {
+            for( Class<?> candidate : candidates )
+            {
+                if( checkClassMatch( candidate, lookedUpType ) )
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        protected abstract boolean checkClassMatch( Class<?> candidate, Class<?> lookedUpType );
+    }
+
+    private static final class ExactTypeLookupSpecification
+        extends AbstractTypeLookupSpecification
+    {
+
+        private ExactTypeLookupSpecification( Type lookedupType )
+        {
+            super( lookedupType );
+        }
+
+        @Override
+        protected boolean checkClassMatch( Class<?> candidate, Class<?> lookedUpType )
+        {
+            return candidate.equals( lookedUpType );
+        }
+    }
+
+    private static final class AssignableTypeLookupSpecification
+        extends AbstractTypeLookupSpecification
+    {
+
+        private AssignableTypeLookupSpecification( Type lookedupType )
+        {
+            super( lookedupType );
+        }
+
+        @Override
+        protected boolean checkClassMatch( Class<?> candidate, Class<?> lookedUpType )
+        {
+            return !candidate.equals( lookedUpType ) && lookedUpType.isAssignableFrom( candidate );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java
new file mode 100644
index 0000000..12893cf
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersInstance.java
@@ -0,0 +1,100 @@
+/*
+ * 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.runtime.structure;
+
+import java.util.List;
+import org.apache.zest.api.common.Visibility;
+import org.apache.zest.api.composite.TransientDescriptor;
+import org.apache.zest.api.entity.EntityDescriptor;
+import org.apache.zest.api.object.ObjectDescriptor;
+import org.apache.zest.api.service.ServiceReference;
+import org.apache.zest.api.value.ValueDescriptor;
+import org.apache.zest.functional.Function;
+import org.apache.zest.spi.module.ModelModule;
+
+import static org.apache.zest.functional.Iterables.*;
+
+/**
+ * JAVADOC
+ */
+public final class UsedLayersInstance
+{
+    private final List<LayerInstance> usedLayerInstances;
+
+    public UsedLayersInstance( List<LayerInstance> usedLayerInstances )
+    {
+        this.usedLayerInstances = usedLayerInstances;
+    }
+
+    /* package */ Iterable<ModelModule<ObjectDescriptor>> visibleObjects()
+    {
+        return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<ObjectDescriptor>>>()
+        {
+            @Override
+            public Iterable<ModelModule<ObjectDescriptor>> map( LayerInstance layerInstance )
+            {
+                return layerInstance.visibleObjects( Visibility.application );
+            }
+        }, usedLayerInstances ) );
+    }
+
+    /* package */ Iterable<ModelModule<TransientDescriptor>> visibleTransients()
+    {
+        return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<TransientDescriptor>>>()
+        {
+            @Override
+            public Iterable<ModelModule<TransientDescriptor>> map( LayerInstance layerInstance )
+            {
+                return layerInstance.visibleTransients( Visibility.application );
+            }
+        }, usedLayerInstances ) );
+    }
+
+    /* package */ Iterable<ModelModule<EntityDescriptor>> visibleEntities()
+    {
+        return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<EntityDescriptor>>>()
+        {
+            @Override
+            public Iterable<ModelModule<EntityDescriptor>> map( LayerInstance layerInstance )
+            {
+                return layerInstance.visibleEntities( Visibility.application );
+            }
+        }, usedLayerInstances ) );
+    }
+
+    /* package */ Iterable<ModelModule<ValueDescriptor>> visibleValues()
+    {
+        return flattenIterables( map( new Function<LayerInstance, Iterable<ModelModule<ValueDescriptor>>>()
+        {
+            @Override
+            public Iterable<ModelModule<ValueDescriptor>> map( LayerInstance layerInstance )
+            {
+                return layerInstance.visibleValues( Visibility.application );
+            }
+        }, usedLayerInstances ) );
+    }
+
+    /* package */ Iterable<ServiceReference<?>> visibleServices()
+    {
+        return flattenIterables( map( new Function<LayerInstance, Iterable<ServiceReference<?>>>()
+        {
+            @Override
+            public Iterable<ServiceReference<?>> map( LayerInstance layerInstance )
+            {
+                return layerInstance.visibleServices( Visibility.application );
+            }
+        }, usedLayerInstances ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java
new file mode 100644
index 0000000..1f098a9
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/UsedLayersModel.java
@@ -0,0 +1,63 @@
+/*
+ * 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.runtime.structure;
+
+import java.util.List;
+import org.apache.zest.api.structure.UsedLayersDescriptor;
+import org.apache.zest.functional.HierarchicalVisitor;
+import org.apache.zest.functional.VisitableHierarchy;
+
+/**
+ * JAVADOC
+ */
+public final class UsedLayersModel
+    implements UsedLayersDescriptor, VisitableHierarchy<Object, Object>
+{
+    private final List<LayerModel> usedLayers;
+
+    public UsedLayersModel( List<LayerModel> usedLayers )
+    {
+        this.usedLayers = usedLayers;
+    }
+
+    @Override
+    public Iterable<LayerModel> layers()
+    {
+        return usedLayers;
+    }
+
+    @Override
+    public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor )
+        throws ThrowableType
+    {
+        if( visitor.visitEnter( this ) )
+        {
+            for( LayerModel usedLayer : usedLayers )
+            {
+                if( !usedLayer.accept( visitor ) )
+                {
+                    break;
+                }
+            }
+        }
+
+        return visitor.visitLeave( this );
+    }
+
+    public UsedLayersInstance newInstance( List<LayerInstance> usedLayerInstances )
+    {
+        return new UsedLayersInstance( usedLayerInstances );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.java
new file mode 100644
index 0000000..bb646eb
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/VisibilitySpecification.java
@@ -0,0 +1,47 @@
+/*
+ * 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.runtime.structure;
+
+import org.apache.zest.api.common.Visibility;
+import org.apache.zest.api.composite.ModelDescriptor;
+import org.apache.zest.functional.Specification;
+
+/**
+ * TODO
+ */
+public class VisibilitySpecification
+    implements Specification<ModelDescriptor>
+{
+    public static final Specification<ModelDescriptor> MODULE = new VisibilitySpecification( Visibility.module );
+    public static final Specification<ModelDescriptor> LAYER = new VisibilitySpecification( Visibility.layer );
+    public static final Specification<ModelDescriptor> APPLICATION = new VisibilitySpecification( Visibility.application );
+
+    private final Visibility visibility;
+
+    public VisibilitySpecification( Visibility visibility )
+    {
+        this.visibility = visibility;
+    }
+
+    @Override
+    public boolean satisfiedBy( ModelDescriptor item )
+    {
+        return item.visibility().ordinal() >= visibility.ordinal();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java b/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java
new file mode 100644
index 0000000..2c7951c
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/types/ValueTypeFactory.java
@@ -0,0 +1,230 @@
+/*
+ * 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.runtime.types;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import org.apache.zest.api.common.InvalidApplicationException;
+import org.apache.zest.api.common.MetaInfo;
+import org.apache.zest.api.common.Visibility;
+import org.apache.zest.api.type.CollectionType;
+import org.apache.zest.api.type.EnumType;
+import org.apache.zest.api.type.MapType;
+import org.apache.zest.api.type.Serialization;
+import org.apache.zest.api.type.ValueCompositeType;
+import org.apache.zest.api.type.ValueType;
+import org.apache.zest.api.util.Classes;
+import org.apache.zest.api.value.ValueComposite;
+import org.apache.zest.functional.HierarchicalVisitorAdapter;
+import org.apache.zest.functional.Iterables;
+import org.apache.zest.functional.Specifications;
+import org.apache.zest.runtime.association.AssociationsModel;
+import org.apache.zest.runtime.association.ManyAssociationsModel;
+import org.apache.zest.runtime.association.NamedAssociationsModel;
+import org.apache.zest.runtime.composite.CompositeMethodsModel;
+import org.apache.zest.runtime.composite.MixinsModel;
+import org.apache.zest.runtime.property.PropertiesModel;
+import org.apache.zest.runtime.structure.LayerModel;
+import org.apache.zest.runtime.structure.ModuleModel;
+import org.apache.zest.runtime.structure.UsedLayersModel;
+import org.apache.zest.runtime.value.ValueModel;
+import org.apache.zest.runtime.value.ValueStateModel;
+import org.apache.zest.runtime.value.ValuesModel;
+
+public class ValueTypeFactory
+{
+    private static final ValueTypeFactory instance = new ValueTypeFactory();
+
+    public static ValueTypeFactory instance()
+    {
+        return instance;
+    }
+
+    @SuppressWarnings( {"raw", "unchecked"} )
+    public ValueType newValueType( Type type,
+                                   Class declaringClass,
+                                   Class compositeType,
+                                   LayerModel layer,
+                                   ModuleModel module,
+                                   Serialization.Variant variant
+    )
+    {
+        ValueType valueType = null;
+        if( CollectionType.isCollection( type ) )
+        {
+            if( type instanceof ParameterizedType )
+            {
+                ParameterizedType pt = (ParameterizedType) type;
+                Type collectionType = pt.getActualTypeArguments()[ 0 ];
+                if( collectionType instanceof TypeVariable )
+                {
+                    TypeVariable collectionTypeVariable = (TypeVariable) collectionType;
+                    collectionType = Classes.resolveTypeVariable( collectionTypeVariable, declaringClass, compositeType );
+                }
+                ValueType collectedType = newValueType( collectionType, declaringClass, compositeType, layer, module, variant );
+                valueType = new CollectionType( Classes.RAW_CLASS.map( type ), collectedType );
+            }
+            else
+            {
+                ValueType collectedType = newValueType( Object.class, declaringClass, compositeType, layer, module, variant );
+                valueType = new CollectionType( Classes.RAW_CLASS.map( type ), collectedType );
+            }
+        }
+        else if( MapType.isMap( type ) )
+        {
+            if( type instanceof ParameterizedType )
+            {
+                ParameterizedType pt = (ParameterizedType) type;
+                Type keyType = pt.getActualTypeArguments()[ 0 ];
+                if( keyType instanceof TypeVariable )
+                {
+                    TypeVariable keyTypeVariable = (TypeVariable) keyType;
+                    keyType = Classes.resolveTypeVariable( keyTypeVariable, declaringClass, compositeType );
+                }
+                ValueType keyedType = newValueType( keyType, declaringClass, compositeType, layer, module, variant );
+                Type valType = pt.getActualTypeArguments()[ 1 ];
+                if( valType instanceof TypeVariable )
+                {
+                    TypeVariable valueTypeVariable = (TypeVariable) valType;
+                    valType = Classes.resolveTypeVariable( valueTypeVariable, declaringClass, compositeType );
+                }
+                ValueType valuedType = newValueType( valType, declaringClass, compositeType, layer, module, variant );
+                valueType = new MapType( Classes.RAW_CLASS.map( type ), keyedType, valuedType, variant );
+            }
+            else
+            {
+                ValueType keyType = newValueType( Object.class, declaringClass, compositeType, layer, module, variant );
+                ValueType valuesType = newValueType( Object.class, declaringClass, compositeType, layer, module, variant );
+                valueType = new MapType( Classes.RAW_CLASS.map( type ), keyType, valuesType, variant );
+            }
+        }
+        else if( ValueCompositeType.isValueComposite( type ) )
+        {
+            // Find ValueModel in module/layer/used layers
+            ValueModel model = new ValueFinder( layer, module, Classes.RAW_CLASS.map( type ) ).getFoundModel();
+
+            if( model == null )
+            {
+                if( type.equals( ValueComposite.class ) )
+                {
+                    // Create default model
+                    MixinsModel mixinsModel = new MixinsModel();
+                    Iterable valueComposite = (Iterable) Iterables.iterable( ValueComposite.class );
+                    ValueStateModel valueStateModel = new ValueStateModel( new PropertiesModel(),
+                                                                           new AssociationsModel(),
+                                                                           new ManyAssociationsModel(),
+                                                                           new NamedAssociationsModel() );
+                    model = new ValueModel( valueComposite, Visibility.application, new MetaInfo(),
+                                            mixinsModel, valueStateModel, new CompositeMethodsModel( mixinsModel ) );
+                }
+                else
+                {
+                    throw new InvalidApplicationException( "[" + module.name() + "] Could not find ValueComposite of type " + type );
+                }
+            }
+
+            return model.valueType();
+        }
+        else if( EnumType.isEnum( type ) )
+        {
+            valueType = new EnumType( Classes.RAW_CLASS.map( type ) );
+        }
+        else
+        {
+            valueType = new ValueType( Classes.RAW_CLASS.map( type ) );
+        }
+
+        return valueType;
+    }
+
+    @SuppressWarnings( "raw" )
+    private static class ValueFinder
+        extends HierarchicalVisitorAdapter<Object, Object, RuntimeException>
+    {
+        private Class type;
+        private ValueModel foundModel;
+        private Visibility visibility;
+
+        private ValueFinder( LayerModel layer, ModuleModel module, Class type )
+        {
+            this.type = type;
+
+            visibility = Visibility.module;
+            module.accept( this );
+
+            if( foundModel == null )
+            {
+                visibility = Visibility.layer;
+                layer.accept( this );
+
+                if( foundModel == null )
+                {
+                    visibility = Visibility.application;
+                    layer.usedLayers().accept( this );
+                }
+            }
+        }
+
+        public ValueModel getFoundModel()
+        {
+            return foundModel;
+        }
+
+        @Override
+        public boolean visitEnter( Object visited )
+            throws RuntimeException
+        {
+            if( visited instanceof ValuesModel )
+            {
+                return true;
+            }
+            else if( visited instanceof ModuleModel )
+            {
+                return true;
+            }
+            else if (visited instanceof LayerModel )
+            {
+                return true;
+            }
+            else if (visited instanceof UsedLayersModel )
+            {
+                return true;
+            }
+            else if( visited instanceof ValueModel )
+            {
+                ValueModel valueModel = (ValueModel) visited;
+                boolean typeEquality = Specifications.in( valueModel.types() ).satisfiedBy( type );
+                if( typeEquality && valueModel.visibility().ordinal() >= visibility.ordinal() )
+                {
+                    foundModel = valueModel;
+                }
+            }
+
+            return false;
+        }
+
+        @Override
+        public boolean visitLeave( Object visited )
+            throws RuntimeException
+        {
+            return foundModel == null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java
new file mode 100644
index 0000000..a390874
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderEntityState.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2009-2011, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2009-2013, Niclas Hedhman. All Rights Reserved.
+ * Copyright (c) 2014, Paul Merlin. All Rights Reserved.
+ *
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License. 
+ */
+package org.apache.zest.runtime.unitofwork;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.zest.api.common.QualifiedName;
+import org.apache.zest.api.entity.EntityDescriptor;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.api.util.Classes;
+import org.apache.zest.spi.entity.EntityState;
+import org.apache.zest.spi.entity.EntityStatus;
+import org.apache.zest.spi.entity.ManyAssociationState;
+import org.apache.zest.spi.entity.NamedAssociationState;
+
+/**
+ * Implementation of EntityState for use through EntityBuilder.
+ */
+public final class BuilderEntityState
+    implements EntityState
+{
+    private final EntityDescriptor entityType;
+    private final EntityReference reference;
+    private final Map<QualifiedName, Object> properties = new HashMap<>();
+    private final Map<QualifiedName, EntityReference> associations = new HashMap<>();
+    private final Map<QualifiedName, ManyAssociationState> manyAssociations = new HashMap<>();
+    private final Map<QualifiedName, NamedAssociationState> namedAssociations = new HashMap<>();
+
+    public BuilderEntityState( EntityDescriptor type, EntityReference reference )
+    {
+        this.entityType = type;
+        this.reference = reference;
+    }
+
+    @Override
+    public EntityReference identity()
+    {
+        return reference;
+    }
+
+    @Override
+    public String version()
+    {
+        return "";
+    }
+
+    @Override
+    public long lastModified()
+    {
+        return 0;
+    }
+
+    @Override
+    public void remove()
+    {
+    }
+
+    @Override
+    public EntityStatus status()
+    {
+        return EntityStatus.NEW;
+    }
+
+    @Override
+    public boolean isAssignableTo( Class<?> type )
+    {
+        return Classes.exactTypeSpecification( type ).satisfiedBy( entityType );
+    }
+
+    @Override
+    public EntityDescriptor entityDescriptor()
+    {
+        return entityType;
+    }
+
+    @Override
+    public Object propertyValueOf( QualifiedName stateName )
+    {
+        return properties.get( stateName );
+    }
+
+    @Override
+    public EntityReference associationValueOf( QualifiedName stateName )
+    {
+        return associations.get( stateName );
+    }
+
+    @Override
+    public void setPropertyValue( QualifiedName stateName, Object newValue )
+    {
+        properties.put( stateName, newValue );
+    }
+
+    @Override
+    public void setAssociationValue( QualifiedName stateName, EntityReference newEntity )
+    {
+        associations.put( stateName, newEntity );
+    }
+
+    @Override
+    public ManyAssociationState manyAssociationValueOf( QualifiedName stateName )
+    {
+        ManyAssociationState state = manyAssociations.get( stateName );
+        if( state == null )
+        {
+            state = new BuilderManyAssociationState();
+            manyAssociations.put( stateName, state );
+        }
+        return state;
+    }
+
+    @Override
+    public NamedAssociationState namedAssociationValueOf( QualifiedName stateName )
+    {
+        NamedAssociationState state = namedAssociations.get( stateName );
+        if( state == null )
+        {
+            state = new BuilderNamedAssociationState();
+            namedAssociations.put( stateName, state );
+        }
+        return state;
+    }
+
+    public void copyTo( EntityState newEntityState )
+    {
+        for( Map.Entry<QualifiedName, Object> fromPropertyEntry : properties.entrySet() )
+        {
+            newEntityState.setPropertyValue( fromPropertyEntry.getKey(), fromPropertyEntry.getValue() );
+        }
+        for( Map.Entry<QualifiedName, EntityReference> fromAssociationEntry : associations.entrySet() )
+        {
+            newEntityState.setAssociationValue( fromAssociationEntry.getKey(), fromAssociationEntry.getValue() );
+        }
+        for( Map.Entry<QualifiedName, ManyAssociationState> fromManyAssociationEntry : manyAssociations.entrySet() )
+        {
+            QualifiedName qName = fromManyAssociationEntry.getKey();
+            ManyAssociationState fromManyAssoc = fromManyAssociationEntry.getValue();
+            ManyAssociationState toManyAssoc = newEntityState.manyAssociationValueOf( qName );
+            for( EntityReference entityReference : fromManyAssoc )
+            {
+                toManyAssoc.add( 0, entityReference );
+            }
+        }
+        for( Map.Entry<QualifiedName, NamedAssociationState> fromNamedAssociationEntry : namedAssociations.entrySet() )
+        {
+            QualifiedName qName = fromNamedAssociationEntry.getKey();
+            NamedAssociationState fromNamedAssoc = fromNamedAssociationEntry.getValue();
+            NamedAssociationState toNamedAssoc = newEntityState.namedAssociationValueOf( qName );
+            for( String name : fromNamedAssoc )
+            {
+                toNamedAssoc.put( name, fromNamedAssoc.get( name ) );
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java
new file mode 100644
index 0000000..d549c2a
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderManyAssociationState.java
@@ -0,0 +1,78 @@
+/*
+ * 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.apache.zest.runtime.unitofwork;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.spi.entity.ManyAssociationState;
+
+/**
+ * Default implementation of ManyAssociationState that also
+ * keeps a list of changes that can be extracted at any time.
+ */
+public final class BuilderManyAssociationState
+    implements ManyAssociationState
+{
+    private List<EntityReference> references;
+
+    public BuilderManyAssociationState()
+    {
+        references = new ArrayList<EntityReference>();
+    }
+
+    @Override
+    public int count()
+    {
+        return references.size();
+    }
+
+    @Override
+    public boolean contains( EntityReference entityReference )
+    {
+        return references.contains( entityReference );
+    }
+
+    @Override
+    public boolean add( int i, EntityReference entityReference )
+    {
+        if( references.contains( entityReference ) )
+        {
+            return false;
+        }
+
+        references.add( i, entityReference );
+        return true;
+    }
+
+    @Override
+    public boolean remove( EntityReference entityReference )
+    {
+        return references.remove( entityReference );
+    }
+
+    @Override
+    public EntityReference get( int i )
+    {
+        return references.get( i );
+    }
+
+    @Override
+    public Iterator<EntityReference> iterator()
+    {
+        return references.iterator();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java
new file mode 100644
index 0000000..8ec7a4b
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/BuilderNamedAssociationState.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2011-2013, Niclas Hedhman. All Rights Reserved.
+ * Copyright (c) 2014, Paul Merlin. All Rights Reserved.
+ *
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License. 
+ */
+package org.apache.zest.runtime.unitofwork;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.spi.entity.NamedAssociationState;
+
+/**
+ * Default implementation of NamedAssociationState that also
+ * keeps a list of changes that can be extracted at any time.
+ */
+public final class BuilderNamedAssociationState
+    implements NamedAssociationState
+{
+    private final Map<String, EntityReference> references;
+
+    public BuilderNamedAssociationState()
+    {
+        references = new HashMap<>();
+    }
+
+    @Override
+    public int count()
+    {
+        return references.size();
+    }
+
+    @Override
+    public boolean containsName( String name )
+    {
+        return references.containsKey( name );
+    }
+
+    @Override
+    public boolean put( String name, EntityReference entityReference )
+    {
+        return references.put( name, entityReference ) != null;
+    }
+
+    @Override
+    public boolean remove( String name )
+    {
+        return references.remove( name ) != null;
+    }
+
+    @Override
+    public EntityReference get( String name )
+    {
+        return references.get( name );
+    }
+
+    @Override
+    public String nameOf( EntityReference entityReference )
+    {
+        for( Map.Entry<String, EntityReference> entry : references.entrySet() )
+        {
+            if( entry.getValue().equals( entityReference ) )
+            {
+                return entry.getKey();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Iterator<String> iterator()
+    {
+        return references.keySet().iterator();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java
new file mode 100644
index 0000000..014c28c
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityBuilderInstance.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2007-2009, Rickard Öberg. All Rights Reserved.
+ * Copyright (c) 2008, Alin Dreghiciu. All Rights Reserved.
+ * Copyright (c) 2008, Edward Yakop. All Rights Reserved.
+ * Copyright (c) 2014-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.runtime.unitofwork;
+
+import org.apache.zest.api.common.QualifiedName;
+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.runtime.composite.FunctionStateResolver;
+import org.apache.zest.runtime.entity.EntityInstance;
+import org.apache.zest.runtime.entity.EntityModel;
+import org.apache.zest.spi.module.ModelModule;
+import org.apache.zest.runtime.structure.ModuleUnitOfWork;
+import org.apache.zest.spi.entity.EntityState;
+import org.apache.zest.spi.entitystore.EntityStoreUnitOfWork;
+
+/**
+ * Implementation of EntityBuilder. Maintains an instance of the entity which
+ * will not have its state validated until it is created by calling newInstance().
+ */
+public final class EntityBuilderInstance<T>
+    implements EntityBuilder<T>
+{
+    private static final QualifiedName IDENTITY_STATE_NAME;
+
+    private final ModelModule<EntityModel> model;
+    private final ModuleUnitOfWork uow;
+    private final EntityStoreUnitOfWork store;
+    private String identity;
+
+    private final BuilderEntityState entityState;
+    private final EntityInstance prototypeInstance;
+
+    static
+    {
+        try
+        {
+            IDENTITY_STATE_NAME = QualifiedName.fromAccessor( Identity.class.getMethod( "identity" ) );
+        }
+        catch( NoSuchMethodException e )
+        {
+            throw new InternalError( "Zest Core Runtime codebase is corrupted. Contact Zest team: EntityBuilderInstance" );
+        }
+    }
+
+    public EntityBuilderInstance(
+        ModelModule<EntityModel> model,
+        ModuleUnitOfWork uow,
+        EntityStoreUnitOfWork store,
+        String identity
+    )
+    {
+        this( model, uow, store, identity, null );
+    }
+
+    public EntityBuilderInstance(
+        ModelModule<EntityModel> model,
+        ModuleUnitOfWork uow,
+        EntityStoreUnitOfWork store,
+        String identity,
+        FunctionStateResolver stateResolver
+    )
+    {
+        this.model = model;
+        this.uow = uow;
+        this.store = store;
+        this.identity = identity;
+        EntityReference reference = new EntityReference( identity );
+        entityState = new BuilderEntityState( model.model(), reference );
+        model.model().initState( model.module(), entityState );
+        if( stateResolver != null )
+        {
+            stateResolver.populateState( model.model(), entityState );
+        }
+        entityState.setPropertyValue( IDENTITY_STATE_NAME, identity );
+        prototypeInstance = model.model().newInstance( uow, model.module(), entityState );
+    }
+
+    @SuppressWarnings( "unchecked" )
+    @Override
+    public T instance()
+    {
+        checkValid();
+        return prototypeInstance.<T>proxy();
+    }
+
+    @Override
+    public <K> K instanceFor( Class<K> mixinType )
+    {
+        checkValid();
+        return prototypeInstance.newProxy( mixinType );
+    }
+
+    @Override
+    @SuppressWarnings( "unchecked" )
+    public T newInstance()
+        throws LifecycleException
+    {
+        checkValid();
+
+        String identity;
+
+        // Figure out whether to use given or generated identity
+        identity = (String) entityState.propertyValueOf( IDENTITY_STATE_NAME );
+        EntityState newEntityState = model.model().newEntityState( store, uow.module(),
+                                                                   EntityReference.parseEntityReference( identity ) );
+
+        prototypeInstance.invokeCreate();
+
+        // Check constraints
+        prototypeInstance.checkConstraints();
+
+        entityState.copyTo( newEntityState );
+
+        EntityInstance instance = model.model().newInstance( uow, model.module(), newEntityState );
+
+        Object proxy = instance.proxy();
+
+        // Add entity in UOW
+        uow.addEntity( instance );
+
+        // Invalidate builder
+        this.identity = null;
+
+        return (T) proxy;
+    }
+
+    private void checkValid()
+        throws IllegalStateException
+    {
+        if( identity == null )
+        {
+            throw new IllegalStateException( "EntityBuilder is not valid after call to newInstance()" );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java
new file mode 100644
index 0000000..6afe68e
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/EntityStateStore.java
@@ -0,0 +1,33 @@
+/*
+ * 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.apache.zest.runtime.unitofwork;
+
+import org.apache.zest.api.association.AssociationStateHolder;
+import org.apache.zest.spi.entity.EntityState;
+
+/**
+ * JAVADOC
+ */
+final class EntityStateStore
+{
+    AssociationStateHolder stateHolder;
+    EntityState state;
+
+    @Override
+    public String toString()
+    {
+        return state.identity().toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java
new file mode 100644
index 0000000..2136528
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/unitofwork/UnitOfWorkInstance.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2007-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.apache.zest.runtime.unitofwork;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+import java.util.concurrent.TimeUnit;
+import org.apache.zest.api.common.MetaInfo;
+import org.apache.zest.api.entity.EntityComposite;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.api.metrics.MetricsCounter;
+import org.apache.zest.api.metrics.MetricsCounterFactory;
+import org.apache.zest.api.metrics.MetricsProvider;
+import org.apache.zest.api.metrics.MetricsTimer;
+import org.apache.zest.api.metrics.MetricsTimerFactory;
+import org.apache.zest.api.unitofwork.ConcurrentEntityModificationException;
+import org.apache.zest.api.unitofwork.EntityTypeNotFoundException;
+import org.apache.zest.api.unitofwork.NoSuchEntityException;
+import org.apache.zest.api.unitofwork.UnitOfWorkCallback;
+import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException;
+import org.apache.zest.api.unitofwork.UnitOfWorkException;
+import org.apache.zest.api.unitofwork.UnitOfWorkOptions;
+import org.apache.zest.api.usecase.Usecase;
+import org.apache.zest.runtime.entity.EntityInstance;
+import org.apache.zest.runtime.entity.EntityModel;
+import org.apache.zest.runtime.structure.ModuleUnitOfWork;
+import org.apache.zest.spi.entity.EntityState;
+import org.apache.zest.spi.entity.EntityStatus;
+import org.apache.zest.spi.entitystore.ConcurrentEntityStateModificationException;
+import org.apache.zest.spi.entitystore.EntityNotFoundException;
+import org.apache.zest.spi.entitystore.EntityStore;
+import org.apache.zest.spi.entitystore.EntityStoreUnitOfWork;
+import org.apache.zest.spi.entitystore.StateCommitter;
+import org.apache.zest.spi.metrics.DefaultMetric;
+import org.apache.zest.spi.module.ModelModule;
+import org.apache.zest.spi.module.ModuleSpi;
+
+import static org.apache.zest.api.unitofwork.UnitOfWorkCallback.UnitOfWorkStatus.COMPLETED;
+import static org.apache.zest.api.unitofwork.UnitOfWorkCallback.UnitOfWorkStatus.DISCARDED;
+import static org.apache.zest.functional.Iterables.map;
+
+public final class UnitOfWorkInstance
+{
+    private static final ThreadLocal<Stack<UnitOfWorkInstance>> current = new ThreadLocal<Stack<UnitOfWorkInstance>>()
+    {
+        @Override
+        protected Stack<UnitOfWorkInstance> initialValue()
+        {
+            return new Stack<>();
+        }
+    };
+    private MetricsTimer.Context metricsTimer;
+
+    public static Stack<UnitOfWorkInstance> getCurrent()
+    {
+        return current.get();
+    }
+
+    private long currentTime;
+    private MetricsProvider metrics;
+    final HashMap<EntityReference, EntityInstance> instanceCache;
+    final HashMap<EntityStore, EntityStoreUnitOfWork> storeUnitOfWork;
+
+    private boolean open;
+
+    private boolean paused;
+
+    /**
+     * Lazy query builder factory.
+     */
+    private Usecase usecase;
+
+    private MetaInfo metaInfo;
+
+    private List<UnitOfWorkCallback> callbacks;
+
+    public UnitOfWorkInstance( Usecase usecase, long currentTime, MetricsProvider metrics )
+    {
+        this.currentTime = currentTime;
+        this.open = true;
+        instanceCache = new HashMap<>();
+        storeUnitOfWork = new HashMap<>();
+        getCurrent().push( this );
+        paused = false;
+        this.usecase = usecase;
+        startCapture( metrics );
+    }
+
+    public long currentTime()
+    {
+        return currentTime;
+    }
+
+    public EntityStoreUnitOfWork getEntityStoreUnitOfWork( EntityStore store, ModuleSpi module )
+    {
+        EntityStoreUnitOfWork uow = storeUnitOfWork.get( store );
+        if( uow == null )
+        {
+            uow = store.newUnitOfWork( usecase, module, currentTime );
+            storeUnitOfWork.put( store, uow );
+        }
+        return uow;
+    }
+
+    public <T> T get( EntityReference identity,
+                      ModuleUnitOfWork uow,
+                      Iterable<ModelModule<EntityModel>> potentialModels,
+                      Class<T> mixinType
+    )
+        throws EntityTypeNotFoundException, NoSuchEntityException
+    {
+        checkOpen();
+
+        EntityInstance entityInstance = instanceCache.get( identity );
+        if( entityInstance == null )
+        {   // Not yet in cache
+
+            // Check if this is a root UoW, or if no parent UoW knows about this entity
+            EntityState entityState = null;
+            EntityModel model = null;
+            ModuleSpi module = null;
+            // Figure out what EntityStore to use
+            for( ModelModule<EntityModel> potentialModel : potentialModels )
+            {
+                EntityStore store = potentialModel.module().entityStore();
+                EntityStoreUnitOfWork storeUow = getEntityStoreUnitOfWork( store, potentialModel.module() );
+                try
+                {
+                    entityState = storeUow.entityStateOf( potentialModel.module(), identity );
+                }
+                catch( EntityNotFoundException e )
+                {
+                    continue;
+                }
+
+                // Get the selected model
+                model = (EntityModel) entityState.entityDescriptor();
+                module = potentialModel.module();
+            }
+
+            // Check if model was found
+            if( model == null )
+            {
+                // Check if state was found
+                if( entityState == null )
+                {
+                    throw new NoSuchEntityException( identity, mixinType, usecase );
+                }
+                else
+                {
+                    throw new EntityTypeNotFoundException( mixinType.getName(),
+                                                           module.name(),
+                                                           map( ModelModule.toStringFunction,
+                                                                module.findVisibleEntityTypes()
+                                                           ) );
+                }
+            }
+
+            // Create instance
+            entityInstance = new EntityInstance( uow, module, model, entityState );
+
+            instanceCache.put( identity, entityInstance );
+        }
+        else
+        {
+            // Check if it has been removed
+            if( entityInstance.status() == EntityStatus.REMOVED )
+            {
+                throw new NoSuchEntityException( identity, mixinType, usecase );
+            }
+        }
+
+        return entityInstance.proxy();
+    }
+
+    public Usecase usecase()
+    {
+        return usecase;
+    }
+
+    public MetaInfo metaInfo()
+    {
+        if( metaInfo == null )
+        {
+            metaInfo = new MetaInfo();
+        }
+
+        return metaInfo;
+    }
+
+    public void pause()
+    {
+        if( !paused )
+        {
+            paused = true;
+            getCurrent().pop();
+
+            UnitOfWorkOptions unitOfWorkOptions = metaInfo().get( UnitOfWorkOptions.class );
+            if( unitOfWorkOptions == null )
+            {
+                unitOfWorkOptions = usecase().metaInfo( UnitOfWorkOptions.class );
+            }
+
+            if( unitOfWorkOptions != null )
+            {
+                if( unitOfWorkOptions.isPruneOnPause() )
+                {
+                    List<EntityReference> prunedInstances = null;
+                    for( EntityInstance entityInstance : instanceCache.values() )
+                    {
+                        if( entityInstance.status() == EntityStatus.LOADED )
+                        {
+                            if( prunedInstances == null )
+                            {
+                                prunedInstances = new ArrayList<>();
+                            }
+                            prunedInstances.add( entityInstance.identity() );
+                        }
+                    }
+                    if( prunedInstances != null )
+                    {
+                        for( EntityReference prunedInstance : prunedInstances )
+                        {
+                            instanceCache.remove( prunedInstance );
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            throw new UnitOfWorkException( "Unit of work is not active" );
+        }
+    }
+
+    public void resume()
+    {
+        if( paused )
+        {
+            paused = false;
+            getCurrent().push( this );
+        }
+        else
+        {
+            throw new UnitOfWorkException( "Unit of work has not been paused" );
+        }
+    }
+
+    public void complete()
+        throws UnitOfWorkCompletionException
+    {
+        checkOpen();
+
+        // Copy list so that it cannot be modified during completion
+        List<UnitOfWorkCallback> currentCallbacks = callbacks == null ? null : new ArrayList<>( callbacks );
+
+        // Commit state to EntityStores
+        List<StateCommitter> committers = applyChanges();
+
+        // Check callbacks
+        notifyBeforeCompletion( currentCallbacks );
+
+        // Commit all changes
+        for( StateCommitter committer : committers )
+        {
+            committer.commit();
+        }
+
+        close();
+
+        // Call callbacks
+        notifyAfterCompletion( currentCallbacks, COMPLETED );
+
+        callbacks = currentCallbacks;
+    }
+
+    public void discard()
+    {
+        if( !isOpen() )
+        {
+            return;
+        }
+        close();
+
+        // Copy list so that it cannot be modified during completion
+        List<UnitOfWorkCallback> currentCallbacks = callbacks == null ? null : new ArrayList<>( callbacks );
+
+        // Call callbacks
+        notifyAfterCompletion( currentCallbacks, DISCARDED );
+
+        for( EntityStoreUnitOfWork entityStoreUnitOfWork : storeUnitOfWork.values() )
+        {
+            entityStoreUnitOfWork.discard();
+        }
+
+        callbacks = currentCallbacks;
+    }
+
+    private void close()
+    {
+        checkOpen();
+
+        if( !isPaused() )
+        {
+            getCurrent().pop();
+        }
+        endCapture();
+        open = false;
+    }
+
+    public boolean isOpen()
+    {
+        return open;
+    }
+
+    public void addUnitOfWorkCallback( UnitOfWorkCallback callback )
+    {
+        if( callbacks == null )
+        {
+            callbacks = new ArrayList<>();
+        }
+
+        callbacks.add( callback );
+    }
+
+    public void removeUnitOfWorkCallback( UnitOfWorkCallback callback )
+    {
+        if( callbacks != null )
+        {
+            callbacks.remove( callback );
+        }
+    }
+
+    public void addEntity( EntityInstance instance )
+    {
+        instanceCache.put( instance.identity(), instance );
+    }
+
+    private List<StateCommitter> applyChanges()
+        throws UnitOfWorkCompletionException
+    {
+        List<StateCommitter> committers = new ArrayList<>();
+        for( EntityStoreUnitOfWork entityStoreUnitOfWork : storeUnitOfWork.values() )
+        {
+            try
+            {
+                StateCommitter committer = entityStoreUnitOfWork.applyChanges();
+                committers.add( committer );
+            }
+            catch( Exception e )
+            {
+                // Cancel all previously prepared stores
+                for( StateCommitter committer : committers )
+                {
+                    committer.cancel();
+                }
+
+                if( e instanceof ConcurrentEntityStateModificationException )
+                {
+                    // If we cancelled due to concurrent modification, then create the proper exception for it!
+                    ConcurrentEntityStateModificationException mee = (ConcurrentEntityStateModificationException) e;
+                    Collection<EntityReference> modifiedEntityIdentities = mee.modifiedEntities();
+                    Collection<EntityComposite> modifiedEntities = new ArrayList<>();
+                    for( EntityReference modifiedEntityIdentity : modifiedEntityIdentities )
+                    {
+                        Collection<EntityInstance> instances = instanceCache.values();
+                        for( EntityInstance instance : instances )
+                        {
+                            if( instance.identity().equals( modifiedEntityIdentity ) )
+                            {
+                                modifiedEntities.add( instance.<EntityComposite>proxy() );
+                            }
+                        }
+                    }
+                    throw new ConcurrentEntityModificationException( modifiedEntities );
+                }
+                else
+                {
+                    throw new UnitOfWorkCompletionException( e );
+                }
+            }
+        }
+        return committers;
+    }
+
+    private void notifyBeforeCompletion( List<UnitOfWorkCallback> callbacks )
+        throws UnitOfWorkCompletionException
+    {
+        // Notify explicitly registered callbacks
+        if( callbacks != null )
+        {
+            for( UnitOfWorkCallback callback : callbacks )
+            {
+                callback.beforeCompletion();
+            }
+        }
+
+        // Notify entities
+        try
+        {
+            for( EntityInstance instance : instanceCache.values() )
+            {
+                boolean isCallback = instance.proxy() instanceof UnitOfWorkCallback;
+                boolean isNotRemoved = !instance.status().equals( EntityStatus.REMOVED );
+                if( isCallback && isNotRemoved )
+                {
+                    UnitOfWorkCallback callback = UnitOfWorkCallback.class.cast( instance.proxy() );
+                    callback.beforeCompletion();
+                }
+            }
+        }
+        catch( UnitOfWorkCompletionException e )
+        {
+            throw e;
+        }
+        catch( Exception e )
+        {
+            throw new UnitOfWorkCompletionException( e );
+        }
+    }
+
+    private void notifyAfterCompletion( List<UnitOfWorkCallback> callbacks,
+                                        final UnitOfWorkCallback.UnitOfWorkStatus status
+    )
+    {
+        if( callbacks != null )
+        {
+            for( UnitOfWorkCallback callback : callbacks )
+            {
+                try
+                {
+                    callback.afterCompletion( status );
+                }
+                catch( Exception e )
+                {
+                    // Ignore
+                }
+            }
+        }
+
+        // Notify entities
+        try
+        {
+            for( EntityInstance instance : instanceCache.values() )
+            {
+                boolean isCallback = instance.proxy() instanceof UnitOfWorkCallback;
+                boolean isNotRemoved = !instance.status().equals( EntityStatus.REMOVED );
+                if( isCallback && isNotRemoved )
+                {
+                    UnitOfWorkCallback callback = UnitOfWorkCallback.class.cast( instance.proxy() );
+                    callback.afterCompletion( status );
+                }
+            }
+        }
+        catch( Exception e )
+        {
+            // Ignore
+        }
+    }
+
+    public void checkOpen()
+    {
+        if( !isOpen() )
+        {
+            throw new UnitOfWorkException( "Unit of work has been closed" );
+        }
+    }
+
+    public boolean isPaused()
+    {
+        return paused;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "UnitOfWork " + hashCode() + "(" + usecase + "): entities:" + instanceCache.size();
+    }
+
+    public void remove( EntityReference entityReference )
+    {
+        instanceCache.remove( entityReference );
+    }
+
+    private void incrementCount()
+    {
+        MetricsCounter counter = getCounter();
+        counter.increment();
+    }
+
+    private void decrementCount()
+    {
+        MetricsCounter counter = getCounter();
+        counter.decrement();
+    }
+
+    private MetricsCounter getCounter()
+    {
+        if( metrics != null )
+        {
+            MetricsCounterFactory metricsFactory = metrics.createFactory( MetricsCounterFactory.class );
+            return metricsFactory.createCounter( getClass(), "UnitOfWork Counter" );
+        }
+        return new DefaultMetric();
+    }
+
+    private void endCapture()
+    {
+        decrementCount();
+        metricsTimer.stop();
+    }
+
+    private void startCapture( MetricsProvider metrics )
+    {
+        this.metrics = metrics;
+        incrementCount();
+        startTimer( metrics );
+    }
+
+    private void startTimer( MetricsProvider metrics )
+    {
+        MetricsTimerFactory metricsFactory = metrics.createFactory( MetricsTimerFactory.class );
+        String name = "UnitOfWork Timer";
+        MetricsTimer timer = metricsFactory.createTimer( getClass(), name, TimeUnit.MILLISECONDS, TimeUnit.SECONDS );
+        metricsTimer = timer.start();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java b/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java
new file mode 100644
index 0000000..6056d9b
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/zest/runtime/value/ManyAssociationValueState.java
@@ -0,0 +1,105 @@
+/*
+ * 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.runtime.value;
+
+import java.util.Iterator;
+import java.util.List;
+import org.apache.zest.api.entity.EntityReference;
+import org.apache.zest.spi.entity.ManyAssociationState;
+
+/**
+ * ManyAssociationState implementation for Value composites.
+ */
+public class ManyAssociationValueState
+    implements ManyAssociationState
+{
+    private List<EntityReference> references;
+
+    public ManyAssociationValueState( List<EntityReference> references )
+    {
+        this.references = references;
+    }
+
+    @Override
+    public int count()
+    {
+        return references.size();
+    }
+
+    @Override
+    public boolean contains( EntityReference entityReference )
+    {
+        return references.contains( entityReference );
+    }
+
+    @Override
+    public boolean add( int i, EntityReference entityReference )
+    {
+        if( references.contains( entityReference ) )
+        {
+            return false;
+        }
+
+        references.add( i, entityReference );
+        return true;
+    }
+
+    @Override
+    public boolean remove( EntityReference entity )
+    {
+        boolean removed = references.remove( entity );
+        return removed;
+    }
+
+    @Override
+    public EntityReference get( int i )
+    {
+        return references.get( i );
+    }
+
+    @Override
+    public Iterator<EntityReference> iterator()
+    {
+        final Iterator<EntityReference> iter = references.iterator();
+
+        return new Iterator<EntityReference>()
+        {
+            EntityReference current;
+
+            @Override
+            public boolean hasNext()
+            {
+                return iter.hasNext();
+            }
+
+            @Override
+            public EntityReference next()
+            {
+                current = iter.next();
+                return current;
+            }
+
+            @Override
+            public void remove()
+            {
+                iter.remove();
+            }
+        };
+    }
+}


Mime
View raw message