http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerInstance.java new file mode 100644 index 0000000..074dccb --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerInstance.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2008, Rickard Ãberg. All Rights Reserved. + * 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.util.ArrayList; +import java.util.List; +import org.apache.zest.api.activation.ActivationEventListener; +import org.apache.zest.api.activation.ActivationException; +import org.apache.zest.api.activation.PassivationException; +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.structure.Layer; +import org.apache.zest.api.value.ValueDescriptor; +import org.apache.zest.functional.Function; +import org.apache.zest.runtime.activation.ActivationDelegate; +import org.apache.zest.spi.module.ModelModule; + +import static org.apache.zest.functional.Iterables.flattenIterables; +import static org.apache.zest.functional.Iterables.map; + +/** + * Instance of a Zest application layer. Contains a list of modules which are managed by this layer. + */ +public class LayerInstance + implements Layer +{ + + // Constructor parameters + private final LayerModel layerModel; + private final ApplicationInstance applicationInstance; + private final UsedLayersInstance usedLayersInstance; + // Eager instance objects + private final ActivationDelegate activation; + private final List<ModuleInstance> moduleInstances; + + public LayerInstance( LayerModel model, + ApplicationInstance applicationInstance, + UsedLayersInstance usedLayersInstance + ) + { + // Constructor parameters + this.layerModel = model; + this.applicationInstance = applicationInstance; + this.usedLayersInstance = usedLayersInstance; + + // Eager instance objects + activation = new ActivationDelegate( this ); + moduleInstances = new ArrayList<>(); + } + + @Override + public String toString() + { + return layerModel.toString(); + } + + // Implementation of Layer + @Override + public String name() + { + return layerModel.name(); + } + + // Implementation of MetaInfoHolder + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return layerModel.metaInfo( infoType ); + } + + // Implementation of Activation + @Override + public void activate() + throws ActivationException + { + activation.activate( layerModel.newActivatorsInstance(), moduleInstances ); + } + + @Override + public void passivate() + throws PassivationException + { + activation.passivate(); + } + + @Override + public void registerActivationEventListener( ActivationEventListener listener ) + { + activation.registerActivationEventListener( listener ); + } + + @Override + public void deregisterActivationEventListener( ActivationEventListener listener ) + { + activation.deregisterActivationEventListener( listener ); + } + + // Other methods + /* package */ void addModule( ModuleInstance module ) + { + module.registerActivationEventListener( activation ); + moduleInstances.add( module ); + } + + /* package */ LayerModel model() + { + return layerModel; + } + + public ApplicationInstance applicationInstance() + { + return applicationInstance; + } + + /* package */ UsedLayersInstance usedLayersInstance() + { + return usedLayersInstance; + } + + /* package */ Iterable<ModelModule<ObjectDescriptor>> visibleObjects( final Visibility visibility ) + { + return flattenIterables( map( new Function<ModuleInstance, Iterable<ModelModule<ObjectDescriptor>>>() + { + + @Override + public Iterable<ModelModule<ObjectDescriptor>> map( ModuleInstance moduleInstance ) + { + return moduleInstance.visibleObjects( visibility ); + } + }, moduleInstances ) ); + } + + /* package */ Iterable<ModelModule<TransientDescriptor>> visibleTransients( final Visibility visibility ) + { + return flattenIterables( map( new Function<ModuleInstance, Iterable<ModelModule<TransientDescriptor>>>() + { + + @Override + public Iterable<ModelModule<TransientDescriptor>> map( ModuleInstance moduleInstance ) + { + return moduleInstance.visibleTransients( visibility ); + } + }, moduleInstances ) ); + } + + /* package */ Iterable<ModelModule<EntityDescriptor>> visibleEntities( final Visibility visibility ) + { + return flattenIterables( map( new Function<ModuleInstance, Iterable<ModelModule<EntityDescriptor>>>() + { + + @Override + public Iterable<ModelModule<EntityDescriptor>> map( ModuleInstance moduleInstance ) + { + return moduleInstance.visibleEntities( visibility ); + } + }, moduleInstances ) ); + } + + /* package */ Iterable<ModelModule<ValueDescriptor>> visibleValues( final Visibility visibility ) + { + return flattenIterables( map( new Function<ModuleInstance, Iterable<ModelModule<ValueDescriptor>>>() + { + + @Override + public Iterable<ModelModule<ValueDescriptor>> map( ModuleInstance moduleInstance ) + { + return moduleInstance.visibleValues( visibility ); + } + }, moduleInstances ) ); + } + + /* package */ Iterable<ServiceReference<?>> visibleServices( final Visibility visibility ) + { + return flattenIterables( map( new Function<ModuleInstance, Iterable<ServiceReference<?>>>() + { + + @Override + public Iterable<ServiceReference<?>> map( ModuleInstance moduleInstance ) + { + return moduleInstance.visibleServices( visibility ); + } + }, moduleInstances ) ); + } + + /* package */ ModuleInstance findModule( String moduleName ) + { + for( ModuleInstance moduleInstance : moduleInstances ) + { + if( moduleInstance.model().name().equals( moduleName ) ) + { + return moduleInstance; + } + } + + throw new IllegalArgumentException( "No such module:" + moduleName ); + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerModel.java new file mode 100644 index 0000000..8592122 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/LayerModel.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2008-2011, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2008-2013, Niclas Hedhman. All Rights Reserved. + * Copyright (c) 2012-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.structure; + +import java.util.List; +import org.apache.zest.api.activation.ActivationException; +import org.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.structure.Layer; +import org.apache.zest.api.structure.LayerDescriptor; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.VisitableHierarchy; +import org.apache.zest.runtime.activation.ActivatorsInstance; +import org.apache.zest.runtime.activation.ActivatorsModel; + +/** + * JAVADOC + */ +public final class LayerModel + implements LayerDescriptor, VisitableHierarchy<Object, Object> +{ + // Model + private final String name; + private final MetaInfo metaInfo; + private final UsedLayersModel usedLayersModel; + private final ActivatorsModel<Layer> activatorsModel; + private final List<ModuleModel> modules; + + public LayerModel( String name, + MetaInfo metaInfo, + UsedLayersModel usedLayersModel, + ActivatorsModel<Layer> activatorsModel, + List<ModuleModel> modules + ) + { + this.name = name; + this.metaInfo = metaInfo; + this.usedLayersModel = usedLayersModel; + this.activatorsModel = activatorsModel; + this.modules = modules; + } + + @Override + public String name() + { + return name; + } + + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + public Iterable<ModuleModel> modules() + { + return modules; + } + + @Override + public UsedLayersModel usedLayers() + { + return usedLayersModel; + } + + public ActivatorsInstance<Layer> newActivatorsInstance() + throws ActivationException + { + return new ActivatorsInstance<>( activatorsModel.newInstances() ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + if( activatorsModel.accept( modelVisitor ) ) + { + for( ModuleModel module : modules ) + { + if( !module.accept( modelVisitor ) ) + { + break; + } + } + } + } + return modelVisitor.visitLeave( this ); + } + + // Context + public LayerInstance newInstance( ApplicationInstance applicationInstance, UsedLayersInstance usedLayerInstance ) + { + LayerInstance layerInstance = new LayerInstance( this, applicationInstance, usedLayerInstance ); + for( ModuleModel module : modules ) + { + ModuleInstance moduleInstance = module.newInstance( layerInstance ); + layerInstance.addModule( moduleInstance ); + } + + return layerInstance; + } + + @Override + public String toString() + { + return name; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleInstance.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleInstance.java new file mode 100644 index 0000000..0a110e4 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleInstance.java @@ -0,0 +1,873 @@ +/* + * Copyright (c) 2008-2012, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2012, Kent Sølvsten. All Rights Reserved. + * Copyright (c) 2008-2013, Niclas Hedhman. All Rights Reserved. + * Copyright (c) 2012-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.structure; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.zest.api.activation.Activation; +import org.apache.zest.api.activation.ActivationEventListener; +import org.apache.zest.api.activation.ActivationException; +import org.apache.zest.api.activation.PassivationException; +import org.apache.zest.api.association.AssociationDescriptor; +import org.apache.zest.api.common.ConstructionException; +import org.apache.zest.api.common.Visibility; +import org.apache.zest.api.composite.AmbiguousTypeException; +import org.apache.zest.api.composite.Composite; +import org.apache.zest.api.composite.ModelDescriptor; +import org.apache.zest.api.composite.NoSuchTransientException; +import org.apache.zest.api.composite.TransientBuilder; +import org.apache.zest.api.composite.TransientDescriptor; +import org.apache.zest.api.entity.EntityComposite; +import org.apache.zest.api.entity.EntityDescriptor; +import org.apache.zest.api.entity.EntityReference; +import org.apache.zest.api.entity.IdentityGenerator; +import org.apache.zest.api.metrics.MetricsProvider; +import org.apache.zest.api.object.NoSuchObjectException; +import org.apache.zest.api.object.ObjectDescriptor; +import org.apache.zest.api.property.Property; +import org.apache.zest.api.property.PropertyDescriptor; +import org.apache.zest.api.query.QueryBuilder; +import org.apache.zest.api.query.QueryBuilderFactory; +import org.apache.zest.api.service.NoSuchServiceException; +import org.apache.zest.api.service.ServiceDescriptor; +import org.apache.zest.api.service.ServiceReference; +import org.apache.zest.api.structure.Module; +import org.apache.zest.api.unitofwork.UnitOfWork; +import org.apache.zest.api.unitofwork.UnitOfWorkException; +import org.apache.zest.api.unitofwork.UnitOfWorkFactory; +import org.apache.zest.api.usecase.Usecase; +import org.apache.zest.api.util.NullArgumentException; +import org.apache.zest.api.value.NoSuchValueException; +import org.apache.zest.api.value.ValueBuilder; +import org.apache.zest.api.value.ValueComposite; +import org.apache.zest.api.value.ValueDescriptor; +import org.apache.zest.api.value.ValueSerialization; +import org.apache.zest.api.value.ValueSerializationException; +import org.apache.zest.functional.Function; +import org.apache.zest.functional.Function2; +import org.apache.zest.functional.Specification; +import org.apache.zest.functional.Specifications; +import org.apache.zest.runtime.activation.ActivationDelegate; +import org.apache.zest.runtime.composite.FunctionStateResolver; +import org.apache.zest.runtime.composite.StateResolver; +import org.apache.zest.runtime.composite.TransientBuilderInstance; +import org.apache.zest.runtime.composite.TransientModel; +import org.apache.zest.runtime.composite.TransientStateInstance; +import org.apache.zest.runtime.composite.TransientsModel; +import org.apache.zest.runtime.composite.UsesInstance; +import org.apache.zest.runtime.entity.EntitiesModel; +import org.apache.zest.runtime.entity.EntityInstance; +import org.apache.zest.runtime.entity.EntityModel; +import org.apache.zest.runtime.injection.InjectionContext; +import org.apache.zest.runtime.object.ObjectModel; +import org.apache.zest.runtime.object.ObjectsModel; +import org.apache.zest.runtime.property.PropertyInstance; +import org.apache.zest.runtime.property.PropertyModel; +import org.apache.zest.runtime.query.QueryBuilderFactoryImpl; +import org.apache.zest.runtime.service.ImportedServicesInstance; +import org.apache.zest.runtime.service.ImportedServicesModel; +import org.apache.zest.runtime.service.ServicesInstance; +import org.apache.zest.runtime.service.ServicesModel; +import org.apache.zest.runtime.unitofwork.UnitOfWorkInstance; +import org.apache.zest.runtime.value.ValueBuilderInstance; +import org.apache.zest.runtime.value.ValueBuilderWithPrototype; +import org.apache.zest.runtime.value.ValueBuilderWithState; +import org.apache.zest.runtime.value.ValueInstance; +import org.apache.zest.runtime.value.ValueModel; +import org.apache.zest.runtime.value.ValuesModel; +import org.apache.zest.spi.entitystore.EntityStore; +import org.apache.zest.spi.metrics.MetricsProviderAdapter; +import org.apache.zest.spi.module.ModelModule; +import org.apache.zest.spi.module.ModuleSpi; +import org.apache.zest.valueserialization.orgjson.OrgJsonValueSerialization; + +import static org.apache.zest.api.util.Classes.RAW_CLASS; +import static org.apache.zest.api.util.Classes.modelTypeSpecification; +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.iterable; +import static org.apache.zest.functional.Iterables.map; +import static org.apache.zest.functional.Iterables.toList; + +/** + * Instance of a Zest Module. Contains the various composites for this Module. + */ +public class ModuleInstance + implements Module, ModuleSpi, Activation +{ + // Constructor parameters + private final ModuleModel model; + private final LayerInstance layer; + private final TransientsModel transients; + private final ValuesModel values; + private final ObjectsModel objects; + private final EntitiesModel entities; + private final ServicesInstance services; + private final ImportedServicesInstance importedServices; + // Eager instance objects + private final ActivationDelegate activation; + private final TypeLookup typeLookup; + private final QueryBuilderFactory queryBuilderFactory; + private final ClassLoader classLoader; + private final EntityFunction entityFunction; + // Lazy assigned on accessors + private EntityStore store; + private IdentityGenerator generator; + private ValueSerialization valueSerialization; + private MetricsProvider metrics; + + @SuppressWarnings( "LeakingThisInConstructor" ) + public ModuleInstance( ModuleModel moduleModel, LayerInstance layerInstance, TransientsModel transientsModel, + EntitiesModel entitiesModel, ObjectsModel objectsModel, ValuesModel valuesModel, + ServicesModel servicesModel, ImportedServicesModel importedServicesModel + ) + { + // Constructor parameters + model = moduleModel; + layer = layerInstance; + transients = transientsModel; + values = valuesModel; + objects = objectsModel; + entities = entitiesModel; + services = servicesModel.newInstance( this ); + importedServices = importedServicesModel.newInstance( this ); + + // Eager instance objects + activation = new ActivationDelegate( this ); + typeLookup = new TypeLookup( this ); + queryBuilderFactory = new QueryBuilderFactoryImpl( this ); + classLoader = new ModuleClassLoader( this, Thread.currentThread().getContextClassLoader() ); + entityFunction = new EntityFunction( this ); + + // Activation + services.registerActivationEventListener( activation ); + importedServices.registerActivationEventListener( activation ); + } + + @Override + public String toString() + { + return model.toString(); + } + + // Implementation of Module + @Override + public String name() + { + return model.name(); + } + + @Override + public ClassLoader classLoader() + { + return classLoader; + } + + @Override + public EntityDescriptor entityDescriptor( String name ) + { + try + { + Class<?> type = classLoader().loadClass( name ); + ModelModule<EntityModel> entityModel = typeLookup.lookupEntityModel( type ); + if( entityModel == null ) + { + return null; + } + return entityModel.model(); + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + @Override + public ObjectDescriptor objectDescriptor( String typeName ) + { + try + { + Class<?> type = classLoader().loadClass( typeName ); + ModelModule<ObjectModel> objectModel = typeLookup.lookupObjectModel( type ); + if( objectModel == null ) + { + return null; + } + return objectModel.model(); + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + @Override + public TransientDescriptor transientDescriptor( String name ) + { + try + { + Class<?> type = classLoader().loadClass( name ); + ModelModule<TransientModel> transientModel = typeLookup.lookupTransientModel( type ); + if( transientModel == null ) + { + return null; + } + return transientModel.model(); + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + @Override + public ValueDescriptor valueDescriptor( String name ) + { + try + { + Class<?> type = classLoader().loadClass( name ); + ModelModule<ValueModel> valueModel = typeLookup.lookupValueModel( type ); + if( valueModel == null ) + { + return null; + } + return valueModel.model(); + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + // Implementation of MetaInfoHolder + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return model.metaInfo( infoType ); + } + + // Implementation of ObjectFactory + @Override + public <T> T newObject( Class<T> mixinType, Object... uses ) + throws NoSuchObjectException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + ModelModule<ObjectModel> modelModule = typeLookup.lookupObjectModel( mixinType ); + + if( modelModule == null ) + { + throw new NoSuchObjectException( mixinType.getName(), name() ); + } + + InjectionContext injectionContext = new InjectionContext( modelModule.module(), UsesInstance.EMPTY_USES.use( uses ) ); + return mixinType.cast( modelModule.model().newInstance( injectionContext ) ); + } + + @Override + public void injectTo( Object instance, Object... uses ) + throws ConstructionException + { + NullArgumentException.validateNotNull( "instance", instance ); + ModelModule<ObjectModel> modelModule = typeLookup.lookupObjectModel( instance.getClass() ); + + if( modelModule == null ) + { + throw new NoSuchObjectException( instance.getClass().getName(), name() ); + } + + InjectionContext injectionContext = new InjectionContext( modelModule.module(), UsesInstance.EMPTY_USES.use( uses ) ); + modelModule.model().inject( injectionContext, instance ); + } + + // Implementation of TransientBuilderFactory + @Override + public <T> TransientBuilder<T> newTransientBuilder( Class<T> mixinType ) + throws NoSuchTransientException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + ModelModule<TransientModel> modelModule = typeLookup.lookupTransientModel( mixinType ); + + if( modelModule == null ) + { + throw new NoSuchTransientException( mixinType.getName(), name() ); + } + + Map<AccessibleObject, Property<?>> properties = new HashMap<>(); + for( PropertyModel propertyModel : modelModule.model().state().properties() ) + { + Property<?> property = new PropertyInstance<>( propertyModel.getBuilderInfo(), + propertyModel.initialValue( modelModule.module() ) ); + properties.put( propertyModel.accessor(), property ); + } + + TransientStateInstance state = new TransientStateInstance( properties ); + + return new TransientBuilderInstance<>( modelModule, state, UsesInstance.EMPTY_USES ); + } + + @Override + public <T> T newTransient( final Class<T> mixinType, Object... uses ) + throws NoSuchTransientException, ConstructionException + { + return newTransientBuilder( mixinType ).use( uses ).newInstance(); + } + + // Implementation of ValueBuilderFactory + @Override + public <T> T newValue( Class<T> mixinType ) + throws NoSuchValueException, ConstructionException + { + return newValueBuilder( mixinType ).newInstance(); + } + + @Override + public <T> ValueBuilder<T> newValueBuilder( Class<T> mixinType ) + throws NoSuchValueException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + ModelModule<ValueModel> compositeModelModule = typeLookup.lookupValueModel( mixinType ); + + if( compositeModelModule == null ) + { + throw new NoSuchValueException( mixinType.getName(), name() ); + } + + StateResolver stateResolver = new InitialStateResolver( compositeModelModule.module() ); + return new ValueBuilderInstance<>( compositeModelModule, this, stateResolver ); + } + + @Override + public <T> ValueBuilder<T> newValueBuilderWithState( Class<T> mixinType, + Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssociationFunction + ) + { + NullArgumentException.validateNotNull( "propertyFunction", propertyFunction ); + NullArgumentException.validateNotNull( "associationFunction", associationFunction ); + NullArgumentException.validateNotNull( "manyAssociationFunction", manyAssociationFunction ); + NullArgumentException.validateNotNull( "namedAssociationFunction", namedAssociationFunction ); + + ModelModule<ValueModel> compositeModelModule = typeLookup.lookupValueModel( mixinType ); + + if( compositeModelModule == null ) + { + throw new NoSuchValueException( mixinType.getName(), name() ); + } + + StateResolver stateResolver = new FunctionStateResolver( + propertyFunction, associationFunction, manyAssociationFunction, namedAssociationFunction + ); + return new ValueBuilderWithState<>( compositeModelModule, this, stateResolver ); + } + + private static class InitialStateResolver + implements StateResolver + { + private final Module module; + + private InitialStateResolver( Module module ) + { + this.module = module; + } + + @Override + public Object getPropertyState( PropertyDescriptor propertyDescriptor ) + { + return propertyDescriptor.initialValue( module ); + } + + @Override + public EntityReference getAssociationState( AssociationDescriptor associationDescriptor ) + { + return null; + } + + @Override + public List<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor ) + { + return new ArrayList<>(); + } + + @Override + public Map<String, EntityReference> getNamedAssociationState( AssociationDescriptor associationDescriptor ) + { + return new HashMap<>(); + } + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> ValueBuilder<T> newValueBuilderWithPrototype( T prototype ) + { + NullArgumentException.validateNotNull( "prototype", prototype ); + + ValueInstance valueInstance = ValueInstance.valueInstanceOf( (ValueComposite) prototype ); + Class<Composite> valueType = (Class<Composite>) first( valueInstance.types() ); + + ModelModule<ValueModel> modelModule = typeLookup.lookupValueModel( valueType ); + + if( modelModule == null ) + { + throw new NoSuchValueException( valueType.getName(), name() ); + } + + return new ValueBuilderWithPrototype<>( modelModule, this, prototype ); + } + + @Override + public <T> T newValueFromSerializedState( Class<T> mixinType, String serializedState ) + throws NoSuchValueException, ConstructionException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + ModelModule<ValueModel> modelModule = typeLookup.lookupValueModel( mixinType ); + + if( modelModule == null ) + { + throw new NoSuchValueException( mixinType.getName(), name() ); + } + + try + { + return valueSerialization().deserialize( modelModule.model().valueType(), serializedState ); + } + catch( ValueSerializationException ex ) + { + throw new ConstructionException( "Could not create value from serialized state", ex ); + } + } + + // Implementation of UnitOfWorkFactory + @Override + public UnitOfWork newUnitOfWork() + { + return newUnitOfWork( Usecase.DEFAULT ); + } + + @Override + public UnitOfWork newUnitOfWork( long currentTime ) + { + return newUnitOfWork( Usecase.DEFAULT, currentTime ); + } + + @Override + public UnitOfWork newUnitOfWork( Usecase usecase ) + { + return newUnitOfWork( usecase == null ? Usecase.DEFAULT : usecase, System.currentTimeMillis() ); + } + + @Override + public UnitOfWork newUnitOfWork( Usecase usecase, long currentTime ) + { + UnitOfWorkInstance unitOfWorkInstance = new UnitOfWorkInstance( usecase, currentTime, metricsProvider() ); + return new ModuleUnitOfWork( ModuleInstance.this, unitOfWorkInstance ); + } + + @Override + public boolean isUnitOfWorkActive() + { + Stack<UnitOfWorkInstance> stack = UnitOfWorkInstance.getCurrent(); + return !stack.isEmpty(); + } + + @Override + public UnitOfWork currentUnitOfWork() + { + Stack<UnitOfWorkInstance> stack = UnitOfWorkInstance.getCurrent(); + if( stack.size() == 0 ) + { + throw new IllegalStateException( "No current UnitOfWork active" ); + } + return new ModuleUnitOfWork( ModuleInstance.this, stack.peek() ); + } + + @Override + public UnitOfWork getUnitOfWork( EntityComposite entity ) + { + EntityInstance instance = EntityInstance.entityInstanceOf( entity ); + return instance.unitOfWork(); + } + + // Implementation of QueryBuilderFactory + @Override + public <T> QueryBuilder<T> newQueryBuilder( final Class<T> resultType ) + { + return queryBuilderFactory.newQueryBuilder( resultType ); + } + + // Implementation of ServiceFinder + @Override + public <T> ServiceReference<T> findService( Class<T> serviceType ) + { + return typeLookup.lookupServiceReference( (Type) serviceType ); + } + + @Override + public <T> ServiceReference<T> findService( Type serviceType ) + { + return typeLookup.lookupServiceReference( serviceType ); + } + + @Override + public <T> Iterable<ServiceReference<T>> findServices( Class<T> serviceType ) + { + return typeLookup.lookupServiceReferences( (Type) serviceType ); + } + + @Override + public <T> Iterable<ServiceReference<T>> findServices( Type serviceType ) + { + return typeLookup.lookupServiceReferences( serviceType ); + } + + // Implementation of Activation + @Override + @SuppressWarnings( "unchecked" ) + public void activate() + throws ActivationException + { + activation.activate( model.newActivatorsInstance(), iterable( services, importedServices ) ); + } + + @Override + public void passivate() + throws PassivationException + { + activation.passivate(); + } + + @Override + public void registerActivationEventListener( ActivationEventListener listener ) + { + activation.registerActivationEventListener( listener ); + } + + @Override + public void deregisterActivationEventListener( ActivationEventListener listener ) + { + activation.deregisterActivationEventListener( listener ); + } + + // Other methods + /* package */ ModuleModel model() + { + return model; + } + + public LayerInstance layerInstance() + { + return layer; + } + + public TypeLookup typeLookup() + { + return typeLookup; + } + + public Function2<EntityReference, Type, Object> getEntityFunction() + { + return entityFunction; + } + + private static class EntityFunction + implements Function2<EntityReference, Type, Object> + { + + private final UnitOfWorkFactory uowf; + + private EntityFunction( UnitOfWorkFactory uowf ) + { + this.uowf = uowf; + } + + @Override + public Object map( EntityReference entityReference, Type type ) + { + return uowf.currentUnitOfWork().get( RAW_CLASS.map( type ), entityReference.identity() ); + } + } + + public EntityStore entityStore() + { + synchronized( this ) + { + if( store == null ) + { + ServiceReference<EntityStore> service = findService( EntityStore.class ); + if( service == null ) + { + throw new UnitOfWorkException( "No EntityStore service available in module " + name() ); + } + store = service.get(); + } + } + return store; + } + + public IdentityGenerator identityGenerator() + { + synchronized( this ) + { + if( generator == null ) + { + ServiceReference<IdentityGenerator> service = findService( IdentityGenerator.class ); + generator = service.get(); + } + return generator; + } + } + + public ValueSerialization valueSerialization() + { + synchronized( this ) + { + if( valueSerialization == null ) + { + try + { + ServiceReference<ValueSerialization> service = findService( ValueSerialization.class ); + valueSerialization = service.get(); + } + catch( NoSuchServiceException e ) + { + valueSerialization = new OrgJsonValueSerialization( layer.applicationInstance(), this, this ); + } + } + } + return valueSerialization; + } + + /* package */ MetricsProvider metricsProvider() + { + synchronized( this ) + { + if( metrics == null ) + { + try + { + ServiceReference<MetricsProvider> service = findService( MetricsProvider.class ); + metrics = service.get(); + } + catch( NoSuchServiceException e ) + { + metrics = new MetricsProviderAdapter(); + } + } + } + return metrics; + } + + public Iterable<ModelModule<ObjectDescriptor>> visibleObjects( Visibility visibility ) + { + return map( ModelModule.<ObjectDescriptor>modelModuleFunction( this ), + filter( new VisibilitySpecification( visibility ), objects.models() ) ); + } + + public Iterable<ModelModule<TransientDescriptor>> visibleTransients( Visibility visibility ) + { + return map( ModelModule.<TransientDescriptor>modelModuleFunction( this ), + filter( new VisibilitySpecification( visibility ), transients.models() ) ); + } + + public Iterable<ModelModule<EntityDescriptor>> visibleEntities( Visibility visibility ) + { + return map( ModelModule.<EntityDescriptor>modelModuleFunction( this ), + filter( new VisibilitySpecification( visibility ), entities.models() ) ); + } + + public Iterable<ModelModule<ValueDescriptor>> visibleValues( Visibility visibility ) + { + return map( ModelModule.<ValueDescriptor>modelModuleFunction( this ), + filter( new VisibilitySpecification( visibility ), values.models() ) ); + } + + public Iterable<ServiceReference<?>> visibleServices( Visibility visibility ) + { + return flatten( services.visibleServices( visibility ), + importedServices.visibleServices( visibility ) ); + } + + // Module ClassLoader + private static class ModuleClassLoader + extends ClassLoader + { + + private final ModuleInstance moduleInstance; + private final Map<String, Class<?>> classes = new ConcurrentHashMap<>(); + + private ModuleClassLoader( ModuleInstance moduleInstance, ClassLoader classLoader ) + { + super( classLoader ); + this.moduleInstance = moduleInstance; + } + + @Override + protected Class<?> findClass( String name ) + throws ClassNotFoundException + { + Class<?> clazz = classes.get( name ); + if( clazz == null ) + { + Specification<ModelDescriptor> modelTypeSpecification = modelTypeSpecification( name ); + Specification<ModelModule<ModelDescriptor>> translate = Specifications.translate( ModelModule.modelFunction(), modelTypeSpecification ); + // Check module + { + Iterable<ModelModule<ModelDescriptor>> i = cast( flatten( + cast( moduleInstance.visibleObjects( Visibility.module ) ), + cast( moduleInstance.visibleEntities( Visibility.module ) ), + cast( moduleInstance.visibleTransients( Visibility.module ) ), + cast( moduleInstance.visibleValues( Visibility.module ) ) ) ); + + Iterable<ModelModule<ModelDescriptor>> moduleModels = filter( translate, i ); + Iterator<ModelModule<ModelDescriptor>> iter = moduleModels.iterator(); + if( iter.hasNext() ) + { + clazz = first( iter.next().model().types() ); + + if( iter.hasNext() ) + { + // Ambiguous exception + throw new ClassNotFoundException( + name, + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + toList( moduleModels ) + ) + ); + } + } + } + + // Check layer + if( clazz == null ) + { + Iterable<ModelModule<ModelDescriptor>> flatten = cast( flatten( + cast( moduleInstance.layerInstance().visibleObjects( Visibility.layer ) ), + cast( moduleInstance.layerInstance().visibleTransients( Visibility.layer ) ), + cast( moduleInstance.layerInstance().visibleEntities( Visibility.layer ) ), + cast( moduleInstance.layerInstance().visibleValues( Visibility.layer ) ), + cast( moduleInstance.layerInstance().visibleObjects( Visibility.application ) ), + cast( moduleInstance.layerInstance().visibleTransients( Visibility.application ) ), + cast( moduleInstance.layerInstance().visibleEntities( Visibility.application ) ), + cast( moduleInstance.layerInstance().visibleValues( Visibility.application ) ) ) ); + Iterable<ModelModule<ModelDescriptor>> layerModels = filter( translate, flatten ); + Iterator<ModelModule<ModelDescriptor>> iter = layerModels.iterator(); + if( iter.hasNext() ) + { + clazz = first( iter.next().model().types() ); + + if( iter.hasNext() ) + { + // Ambiguous exception + throw new ClassNotFoundException( + name, + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + toList( layerModels ) ) + ); + } + } + } + + // Check used layers + if( clazz == null ) + { + Iterable<ModelModule<ModelDescriptor>> flatten = cast( flatten( + cast( moduleInstance.layerInstance().usedLayersInstance().visibleObjects() ), + cast( moduleInstance.layerInstance().usedLayersInstance().visibleTransients() ), + cast( moduleInstance.layerInstance().usedLayersInstance().visibleEntities() ), + cast( moduleInstance.layerInstance().usedLayersInstance().visibleValues() ) ) ); + Iterable<ModelModule<ModelDescriptor>> usedLayersModels = filter( translate, flatten ); + Iterator<ModelModule<ModelDescriptor>> iter = usedLayersModels.iterator(); + if( iter.hasNext() ) + { + clazz = first( iter.next().model().types() ); + + if( iter.hasNext() ) + { + // Ambiguous exception + throw new ClassNotFoundException( + name, + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + toList( usedLayersModels ) + ) + ); + } + } + } + + if( clazz == null ) + { + throw new ClassNotFoundException( name ); + } + classes.put( name, clazz ); + } + + return clazz; + } + } + + public Iterable<ModelModule<ValueDescriptor>> findVisibleValueTypes() + { + return flatten( visibleValues( Visibility.module ), + layerInstance().visibleValues( Visibility.layer ), + layerInstance().visibleValues( Visibility.application ), + layerInstance().usedLayersInstance().visibleValues() + ); + } + + public Iterable<ModelModule<EntityDescriptor>> findVisibleEntityTypes() + { + return flatten( visibleEntities( Visibility.module ), + layerInstance().visibleEntities( Visibility.layer ), + layerInstance().visibleEntities( Visibility.application ), + layerInstance().usedLayersInstance().visibleEntities() + ); + } + public Iterable<ModelModule<TransientDescriptor>> findVisibleTransientTypes() + { + return flatten( visibleTransients( Visibility.module ), + layerInstance().visibleTransients( Visibility.layer ), + layerInstance().visibleTransients( Visibility.application ), + layerInstance().usedLayersInstance().visibleTransients() + ); + } + public Iterable<ModelModule<ServiceDescriptor>> findVisibleServiceTypes() + { + return flatten( visibleServices( Visibility.module ), + layerInstance().visibleServices( Visibility.layer ), + layerInstance().visibleServices( Visibility.application ), + layerInstance().usedLayersInstance().visibleServices() + ); + } + public Iterable<ModelModule<ObjectDescriptor>> findVisibleObjectTypes() + { + return flatten( visibleObjects( Visibility.module ), + layerInstance().visibleObjects( Visibility.layer ), + layerInstance().visibleObjects( Visibility.application ), + layerInstance().usedLayersInstance().visibleObjects() + ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleModel.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleModel.java new file mode 100644 index 0000000..49e3670 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleModel.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2008-2011, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2008-2013, Niclas Hedhman. All Rights Reserved. + * Copyright (c) 2012-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.structure; + +import org.apache.zest.api.activation.ActivationException; +import org.apache.zest.api.common.MetaInfo; +import org.apache.zest.api.structure.Module; +import org.apache.zest.api.structure.ModuleDescriptor; +import org.apache.zest.functional.HierarchicalVisitor; +import org.apache.zest.functional.VisitableHierarchy; +import org.apache.zest.runtime.activation.ActivatorsInstance; +import org.apache.zest.runtime.activation.ActivatorsModel; +import org.apache.zest.runtime.composite.TransientsModel; +import org.apache.zest.runtime.entity.EntitiesModel; +import org.apache.zest.runtime.object.ObjectsModel; +import org.apache.zest.runtime.service.ImportedServicesModel; +import org.apache.zest.runtime.service.ServicesModel; +import org.apache.zest.runtime.value.ValuesModel; + +/** + * JAVADOC + */ +public class ModuleModel + implements ModuleDescriptor, VisitableHierarchy<Object, Object> +{ + private final ActivatorsModel<Module> activatorsModel; + private final TransientsModel transientsModel; + private final EntitiesModel entitiesModel; + private final ObjectsModel objectsModel; + private final ValuesModel valuesModel; + private final ServicesModel servicesModel; + private final ImportedServicesModel importedServicesModel; + + private final String name; + private final MetaInfo metaInfo; + + public ModuleModel( String name, + MetaInfo metaInfo, + ActivatorsModel<Module> activatorsModel, + TransientsModel transientsModel, + EntitiesModel entitiesModel, + ObjectsModel objectsModel, + ValuesModel valuesModel, + ServicesModel servicesModel, + ImportedServicesModel importedServicesModel + ) + { + this.name = name; + this.metaInfo = metaInfo; + this.activatorsModel = activatorsModel; + this.transientsModel = transientsModel; + this.entitiesModel = entitiesModel; + this.objectsModel = objectsModel; + this.valuesModel = valuesModel; + this.servicesModel = servicesModel; + this.importedServicesModel = importedServicesModel; + } + + @Override + public String name() + { + return name; + } + + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + public ActivatorsInstance<Module> newActivatorsInstance() + throws ActivationException + { + return new ActivatorsInstance<>( activatorsModel.newInstances() ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + if( activatorsModel.accept( modelVisitor ) ) + { + if( transientsModel.accept( modelVisitor ) ) + { + if( entitiesModel.accept( modelVisitor ) ) + { + if( servicesModel.accept( modelVisitor ) ) + { + if( importedServicesModel.accept( modelVisitor ) ) + { + if( objectsModel.accept( modelVisitor ) ) + { + valuesModel.accept( modelVisitor ); + } + } + } + } + } + } + } + return modelVisitor.visitLeave( this ); + } + + // Context + + public ModuleInstance newInstance( LayerInstance layerInstance ) + { + return new ModuleInstance( this, layerInstance, transientsModel, entitiesModel, objectsModel, valuesModel, servicesModel, importedServicesModel ); + } + + @Override + public String toString() + { + return name; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/8744a67f/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleUnitOfWork.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleUnitOfWork.java b/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleUnitOfWork.java new file mode 100644 index 0000000..f052de2 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/zest/runtime/structure/ModuleUnitOfWork.java @@ -0,0 +1,773 @@ +/* + * Copyright (c) 2009, Rickard Ãberg. All Rights Reserved. + * Copyright (c) 2013-2015, Niclas Hedhman. All Rights Reserved. + * Copyright (c) 2013-2015, Paul Merlin. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.zest.runtime.structure; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.apache.zest.api.association.AssociationDescriptor; +import org.apache.zest.api.association.AssociationStateHolder; +import org.apache.zest.api.common.QualifiedName; +import org.apache.zest.api.composite.Composite; +import org.apache.zest.api.entity.EntityBuilder; +import org.apache.zest.api.entity.EntityComposite; +import org.apache.zest.api.entity.EntityReference; +import org.apache.zest.api.entity.Identity; +import org.apache.zest.api.entity.IdentityGenerator; +import org.apache.zest.api.entity.LifecycleException; +import org.apache.zest.api.property.Property; +import org.apache.zest.api.property.PropertyDescriptor; +import org.apache.zest.api.property.StateHolder; +import org.apache.zest.api.query.Query; +import org.apache.zest.api.query.QueryBuilder; +import org.apache.zest.api.query.QueryExecutionException; +import org.apache.zest.api.query.grammar.OrderBy; +import org.apache.zest.api.service.NoSuchServiceException; +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.UnitOfWork; +import org.apache.zest.api.unitofwork.UnitOfWorkCallback; +import org.apache.zest.api.unitofwork.UnitOfWorkCompletionException; +import org.apache.zest.api.unitofwork.UnitOfWorkFactory; +import org.apache.zest.api.usecase.Usecase; +import org.apache.zest.api.util.NullArgumentException; +import org.apache.zest.api.value.ValueBuilder; +import org.apache.zest.api.value.ValueComposite; +import org.apache.zest.functional.Function; +import org.apache.zest.functional.Iterables; +import org.apache.zest.functional.Specification; +import org.apache.zest.runtime.association.AssociationInstance; +import org.apache.zest.runtime.association.ManyAssociationInstance; +import org.apache.zest.runtime.association.NamedAssociationInstance; +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.runtime.property.PropertyModel; +import org.apache.zest.runtime.unitofwork.EntityBuilderInstance; +import org.apache.zest.runtime.unitofwork.UnitOfWorkInstance; +import org.apache.zest.runtime.value.ValueInstance; +import org.apache.zest.spi.entity.EntityState; +import org.apache.zest.spi.entity.EntityStatus; +import org.apache.zest.spi.entity.NamedAssociationState; +import org.apache.zest.spi.entitystore.EntityStore; +import org.apache.zest.spi.module.ModelModule; +import org.apache.zest.spi.query.EntityFinder; +import org.apache.zest.spi.query.EntityFinderException; +import org.apache.zest.spi.query.QueryBuilderSPI; +import org.apache.zest.spi.query.QuerySource; + +import static org.apache.zest.api.entity.EntityReference.parseEntityReference; +import static org.apache.zest.functional.Iterables.first; +import static org.apache.zest.functional.Iterables.map; + +/** + * JAVADOC + */ +public class ModuleUnitOfWork + implements UnitOfWork +{ + private static final QualifiedName IDENTITY_STATE_NAME; + + static + { + try + { + IDENTITY_STATE_NAME = QualifiedName.fromAccessor( Identity.class.getMethod( "identity" ) ); + } + catch( NoSuchMethodException e ) + { + throw new InternalError( "Zest Core Runtime codebase is corrupted. Contact Zest team: ModuleUnitOfWork" ); + } + } + + private final UnitOfWorkInstance uow; + private final ModuleInstance module; + + ModuleUnitOfWork( ModuleInstance module, UnitOfWorkInstance uow ) + { + this.module = module; + this.uow = uow; + } + + public ModuleInstance module() + { + return module; + } + + public UnitOfWorkInstance instance() + { + return uow; + } + + @Override + public UnitOfWorkFactory unitOfWorkFactory() + { + return module; + } + + @Override + public long currentTime() + { + return uow.currentTime(); + } + + @Override + public Usecase usecase() + { + return uow.usecase(); + } + + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return uow.metaInfo().get( infoType ); + } + + @Override + public void setMetaInfo( Object metaInfo ) + { + uow.metaInfo().set( metaInfo ); + } + + @Override + @SuppressWarnings( { "raw", "unchecked" } ) + public <T> Query<T> newQuery( QueryBuilder<T> queryBuilder ) + { + QueryBuilderSPI queryBuilderSPI = (QueryBuilderSPI) queryBuilder; + + return queryBuilderSPI.newQuery( new UoWQuerySource( this ) ); + } + + @Override + public <T> T newEntity( Class<T> type ) + throws EntityTypeNotFoundException, LifecycleException + { + return newEntity( type, null ); + } + + @Override + public <T> T newEntity( Class<T> type, String identity ) + throws EntityTypeNotFoundException, LifecycleException + { + return newEntityBuilder( type, identity ).newInstance(); + } + + @Override + public <T> EntityBuilder<T> newEntityBuilder( Class<T> type ) + throws EntityTypeNotFoundException + { + return newEntityBuilder( type, null ); + } + + @Override + public <T> EntityBuilder<T> newEntityBuilder( Class<T> type, String identity ) + throws EntityTypeNotFoundException + { + ModelModule<EntityModel> model = module.typeLookup().lookupEntityModel( type ); + + if( model == null ) + { + throw new EntityTypeNotFoundException( type.getName(), + module.name(), + map( ModelModule.toStringFunction, + module.findVisibleEntityTypes() + ) ); + } + + EntityStore entityStore = model.module().entityStore(); + + // Generate id if necessary + if( identity == null ) + { + IdentityGenerator idGen = model.module().identityGenerator(); + if( idGen == null ) + { + throw new NoSuchServiceException( IdentityGenerator.class.getName(), model.module().name() ); + } + identity = idGen.generate( first( model.model().types() ) ); + } + EntityBuilder<T> builder; + + builder = new EntityBuilderInstance<>( model, + this, + uow.getEntityStoreUnitOfWork( entityStore, module ), + identity ); + return builder; + } + + @Override + public <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 + { + return newEntityBuilderWithState( type, null, + propertyFunction, + associationFunction, + manyAssociationFunction, + namedAssociationFunction ); + } + + @Override + public <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 + { + NullArgumentException.validateNotNull( "propertyFunction", propertyFunction ); + NullArgumentException.validateNotNull( "associationFunction", associationFunction ); + NullArgumentException.validateNotNull( "manyAssociationFunction", manyAssociationFunction ); + NullArgumentException.validateNotNull( "namedAssociationFunction", namedAssociationFunction ); + + ModelModule<EntityModel> model = module.typeLookup().lookupEntityModel( type ); + + if( model == null ) + { + throw new EntityTypeNotFoundException( type.getName(), + module.name(), + map( ModelModule.toStringFunction, + module.findVisibleEntityTypes() + ) ); + } + + EntityStore entityStore = model.module().entityStore(); + + FunctionStateResolver stateResolver = new FunctionStateResolver( + propertyFunction, associationFunction, manyAssociationFunction, namedAssociationFunction + ); + + if( identity == null ) + { + // Use identity from StateResolver if available + PropertyModel identityModel = model.model().state().findPropertyModelByQualifiedName( IDENTITY_STATE_NAME ); + identity = (String) stateResolver.getPropertyState( identityModel ); + if( identity == null ) + { + // Generate identity + IdentityGenerator idGen = model.module().identityGenerator(); + if( idGen == null ) + { + throw new NoSuchServiceException( IdentityGenerator.class.getName(), model.module().name() ); + } + identity = idGen.generate( first( model.model().types() ) ); + } + } + + return new EntityBuilderInstance<>( model, + this, + uow.getEntityStoreUnitOfWork( entityStore, module ), + identity, + stateResolver ); + } + + @Override + public <T> T get( Class<T> type, String identity ) + throws EntityTypeNotFoundException, NoSuchEntityException + { + Iterable<ModelModule<EntityModel>> models = module.typeLookup().lookupEntityModels( type ); + + if( !models.iterator().hasNext() ) + { + throw new EntityTypeNotFoundException( type.getName(), + module.name(), + map( ModelModule.toStringFunction, + module.findVisibleEntityTypes() + ) ); + } + + return uow.get( parseEntityReference( identity ), this, models, type ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> T get( T entity ) + throws EntityTypeNotFoundException + { + EntityComposite entityComposite = (EntityComposite) entity; + EntityInstance compositeInstance = EntityInstance.entityInstanceOf( entityComposite ); + ModelModule<EntityModel> model = new ModelModule<>( compositeInstance.module(), compositeInstance.entityModel() ); + Class<T> type = (Class<T>) first( compositeInstance.types() ); + return uow.get( compositeInstance.identity(), this, Collections.singletonList( model ), type ); + } + + @Override + public void remove( Object entity ) + throws LifecycleException + { + uow.checkOpen(); + + EntityComposite entityComposite = (EntityComposite) entity; + + EntityInstance compositeInstance = EntityInstance.entityInstanceOf( entityComposite ); + + if( compositeInstance.status() == EntityStatus.NEW ) + { + compositeInstance.remove( this ); + uow.remove( compositeInstance.identity() ); + } + else if( compositeInstance.status() == EntityStatus.LOADED || compositeInstance.status() == EntityStatus.UPDATED ) + { + compositeInstance.remove( this ); + } + else + { + throw new NoSuchEntityException( compositeInstance.identity(), compositeInstance.types(), usecase() ); + } + } + + @SuppressWarnings( "DuplicateThrows" ) + @Override + public void complete() + throws UnitOfWorkCompletionException, ConcurrentEntityModificationException + { + uow.complete(); + } + + @Override + public void discard() + { + uow.discard(); + } + + @Override + public void close() + { + discard(); + } + + @Override + public boolean isOpen() + { + return uow.isOpen(); + } + + @Override + public boolean isPaused() + { + return uow.isPaused(); + } + + @Override + public void pause() + { + uow.pause(); + } + + @Override + public void resume() + { + uow.resume(); + } + + @Override + public void addUnitOfWorkCallback( UnitOfWorkCallback callback ) + { + uow.addUnitOfWorkCallback( callback ); + } + + @Override + public void removeUnitOfWorkCallback( UnitOfWorkCallback callback ) + { + uow.removeUnitOfWorkCallback( callback ); + } + + @Override + public boolean equals( Object o ) + { + if( this == o ) + { + return true; + } + if( o == null || getClass() != o.getClass() ) + { + return false; + } + + ModuleUnitOfWork that = (ModuleUnitOfWork) o; + + return uow.equals( that.uow ); + } + + @Override + public int hashCode() + { + return uow.hashCode(); + } + + @Override + public String toString() + { + return uow.toString(); + } + + public void addEntity( EntityInstance instance ) + { + uow.addEntity( instance ); + } + + @Override + public <T extends Identity> T toValue( Class<T> primaryType, T entityComposite ) + { + Function<PropertyDescriptor, Object> propertyFunction = new ToValuePropertyMappingFunction( entityComposite ); + Function<AssociationDescriptor, EntityReference> assocationFunction = new ToValueAssociationMappingFunction<>( entityComposite ); + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssocFunction = new ToValueManyAssociationMappingFunction<>( entityComposite ); + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssocFunction = new ToValueNameAssociationMappingFunction<>( entityComposite ); + + @SuppressWarnings( "unchecked" ) + ValueBuilder<T> builder = module().newValueBuilderWithState( + primaryType, propertyFunction, assocationFunction, manyAssocFunction, namedAssocFunction ); + return builder.newInstance(); + } + + @Override + public <T extends Identity> T toEntity( Class<T> primaryType, T valueComposite ) + { + Function<PropertyDescriptor, Object> propertyFunction = new ToEntityPropertyMappingFunction<>( valueComposite ); + Function<AssociationDescriptor, EntityReference> assocationFunction = new ToEntityAssociationMappingFunction<>( valueComposite ); + Function<AssociationDescriptor, Iterable<EntityReference>> manyAssocFunction = new ToEntityManyAssociationMappingFunction<>( valueComposite ); + Function<AssociationDescriptor, Map<String, EntityReference>> namedAssocFunction = new ToEntityNameAssociationMappingFunction<>( valueComposite ); + + String identity = valueComposite.identity().get(); + try + { + T entity = get( primaryType, identity ); + // If successful, then this entity is to by modified. + EntityInstance instance = EntityInstance.entityInstanceOf( (EntityComposite) entity ); + EntityState state = instance.entityState(); + FunctionStateResolver stateResolver = new FunctionStateResolver( propertyFunction, + assocationFunction, + manyAssocFunction, + namedAssocFunction ); + EntityModel model = (EntityModel) EntityInstance.entityInstanceOf( (EntityComposite) entity ).descriptor(); + stateResolver.populateState( model, state ); + return entity; + } + catch( NoSuchEntityException e ) + { + EntityBuilder<T> entityBuilder = newEntityBuilderWithState( primaryType, + identity, + propertyFunction, + assocationFunction, + manyAssocFunction, + namedAssocFunction ); + return entityBuilder.newInstance(); + } + } + + private static class UoWQuerySource implements QuerySource + { + private final ModuleUnitOfWork moduleUnitOfWork; + + private UoWQuerySource( ModuleUnitOfWork moduleUnitOfWork ) + { + this.moduleUnitOfWork = moduleUnitOfWork; + } + + @Override + public <T> T find( Class<T> resultType, + Specification<Composite> whereClause, + Iterable<OrderBy> orderBySegments, + Integer firstResult, + Integer maxResults, + Map<String, Object> variables + ) + { + final EntityFinder entityFinder = moduleUnitOfWork.module().findService( EntityFinder.class ).get(); + + try + { + final EntityReference foundEntity = entityFinder.findEntity( resultType, whereClause, variables == null ? Collections + .<String, Object>emptyMap() : variables ); + if( foundEntity != null ) + { + try + { + return moduleUnitOfWork.get( resultType, foundEntity.identity() ); + } + catch( NoSuchEntityException e ) + { + return null; // Index is out of sync - entity has been removed + } + } + // No entity was found + return null; + } + catch( EntityFinderException e ) + { + throw new QueryExecutionException( "Finder caused exception", e ); + } + } + + @Override + public <T> long count( Class<T> resultType, + Specification<Composite> whereClause, + Iterable<OrderBy> orderBySegments, + Integer firstResult, + Integer maxResults, + Map<String, Object> variables + ) + { + final EntityFinder entityFinder = moduleUnitOfWork.module().findService( EntityFinder.class ).get(); + + try + { + return entityFinder.countEntities( resultType, whereClause, variables == null ? Collections.<String, Object>emptyMap() : variables ); + } + catch( EntityFinderException e ) + { + e.printStackTrace(); + return 0; + } + } + + @Override + public <T> Iterator<T> iterator( final Class<T> resultType, + Specification<Composite> whereClause, + Iterable<OrderBy> orderBySegments, + Integer firstResult, + Integer maxResults, + Map<String, Object> variables + ) + { + final EntityFinder entityFinder = moduleUnitOfWork.module().findService( EntityFinder.class ).get(); + + try + { + final Iterator<EntityReference> foundEntities = entityFinder.findEntities( resultType, + whereClause, + Iterables.toArray( OrderBy.class, orderBySegments ), + firstResult, + maxResults, + variables == null ? Collections + .<String, Object>emptyMap() : variables ) + .iterator(); + + return new Iterator<T>() + { + @Override + public boolean hasNext() + { + return foundEntities.hasNext(); + } + + @Override + public T next() + { + final EntityReference foundEntity = foundEntities.next(); + try + { + return moduleUnitOfWork.get( resultType, foundEntity.identity() ); + } + catch( NoSuchEntityException e ) + { + // Index is out of sync - entity has been removed + return null; + } + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + catch( EntityFinderException e ) + { + throw new QueryExecutionException( "Query '" + toString() + "' could not be executed", e ); + } + } + + @Override + public String toString() + { + return "UnitOfWork( " + moduleUnitOfWork.usecase().name() + " )"; + } + } + + private class ToValuePropertyMappingFunction + implements Function<PropertyDescriptor, Object> + { + private Object entity; + + public ToValuePropertyMappingFunction( Object entity ) + { + this.entity = entity; + } + + @Override + public Object map( PropertyDescriptor propertyDescriptor ) + { + EntityState entityState = EntityInstance.entityInstanceOf( (EntityComposite) entity ).entityState(); + return entityState.propertyValueOf( propertyDescriptor.qualifiedName() ); + } + } + + private class ToValueAssociationMappingFunction<T> + implements Function<AssociationDescriptor, EntityReference> + { + private final T entity; + + public ToValueAssociationMappingFunction( T entity ) + { + this.entity = entity; + } + + @Override + public EntityReference map( AssociationDescriptor associationDescriptor ) + { + EntityState entityState = EntityInstance.entityInstanceOf( (EntityComposite) entity ).entityState(); + return entityState.associationValueOf( associationDescriptor.qualifiedName() ); + } + } + + private class ToValueManyAssociationMappingFunction<T> + implements Function<AssociationDescriptor, Iterable<EntityReference>> + { + private final T entity; + + public ToValueManyAssociationMappingFunction( T entity ) + { + this.entity = entity; + } + + @Override + public Iterable<EntityReference> map( AssociationDescriptor associationDescriptor ) + { + EntityState entityState = EntityInstance.entityInstanceOf( (EntityComposite) entity ).entityState(); + return entityState.manyAssociationValueOf( associationDescriptor.qualifiedName() ); + } + } + + private class ToValueNameAssociationMappingFunction<T> + implements Function<AssociationDescriptor, Map<String, EntityReference>> + { + private final T entity; + + public ToValueNameAssociationMappingFunction( T entity ) + { + this.entity = entity; + } + + @Override + public Map<String, EntityReference> map( AssociationDescriptor associationDescriptor ) + { + Map<String, EntityReference> result = new HashMap<>(); + EntityState entityState = EntityInstance.entityInstanceOf( (EntityComposite) entity ).entityState(); + final NamedAssociationState state = entityState.namedAssociationValueOf( associationDescriptor.qualifiedName() ); + for( String name : state ) + { + result.put( name, state.get( name ) ); + } + return result; + } + } + + private class ToEntityPropertyMappingFunction<T> + implements Function<PropertyDescriptor, Object> + { + private final T value; + + public ToEntityPropertyMappingFunction( T value ) + { + this.value = value; + } + + @Override + public Object map( PropertyDescriptor propertyDescriptor ) + { + StateHolder state = ValueInstance.valueInstanceOf( (ValueComposite) value ).state(); + Property<Object> property = state.propertyFor( propertyDescriptor.accessor() ); + return property.get(); + } + } + + private class ToEntityAssociationMappingFunction<T> + implements Function<AssociationDescriptor, EntityReference> + { + + private final T value; + + public ToEntityAssociationMappingFunction( T value ) + { + this.value = value; + } + + @Override + public EntityReference map( AssociationDescriptor associationDescriptor ) + { + AssociationStateHolder state = ValueInstance.valueInstanceOf( (ValueComposite) value ).state(); + AssociationInstance<T> association = (AssociationInstance<T>) state.associationFor( associationDescriptor.accessor() ); + return association.getAssociationState().get(); + } + } + + private class ToEntityManyAssociationMappingFunction<T> + implements Function<AssociationDescriptor, Iterable<EntityReference>> + { + + private final T value; + + public ToEntityManyAssociationMappingFunction( T valueComposite ) + { + this.value = valueComposite; + } + + @Override + public Iterable<EntityReference> map( AssociationDescriptor associationDescriptor ) + { + AssociationStateHolder state = ValueInstance.valueInstanceOf( (ValueComposite) value ).state(); + ManyAssociationInstance<T> association = + (ManyAssociationInstance<T>) state.manyAssociationFor( associationDescriptor.accessor() ); + return association.getManyAssociationState(); + } + } + + private class ToEntityNameAssociationMappingFunction<T> + implements Function<AssociationDescriptor, Map<String, EntityReference>> + { + private final T value; + + public ToEntityNameAssociationMappingFunction( T valueComposite ) + { + this.value = valueComposite; + } + + @Override + public Map<String, EntityReference> map( AssociationDescriptor associationDescriptor ) + { + AssociationStateHolder state = ValueInstance.valueInstanceOf( (ValueComposite) value ).state(); + NamedAssociationInstance<T> association = + (NamedAssociationInstance<T>) state.namedAssociationFor( associationDescriptor.accessor() ); + HashMap<String, EntityReference> result = new HashMap<>(); + for( Map.Entry<String, EntityReference> entry : association.getEntityReferences() ) + { + result.put( entry.getKey(), entry.getValue() ); + } + return result; + } + } +}
