http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersInstance.java new file mode 100644 index 0000000..e4d3880 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersInstance.java @@ -0,0 +1,74 @@ +/* + * 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.polygene.runtime.structure; + +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.composite.ModelDescriptor; +import org.apache.polygene.api.composite.TransientDescriptor; +import org.apache.polygene.api.entity.EntityDescriptor; +import org.apache.polygene.api.object.ObjectDescriptor; +import org.apache.polygene.api.structure.LayerDescriptor; +import org.apache.polygene.api.value.ValueDescriptor; + +/** + * JAVADOC + */ +public final class UsedLayersInstance +{ + private final List<LayerDescriptor> usedLayerInstances; + + public UsedLayersInstance( List<LayerDescriptor> usedLayerInstances ) + { + this.usedLayerInstances = usedLayerInstances; + } + + Stream<? extends ObjectDescriptor> visibleObjects() + { + return usedLayerInstances.stream() + .flatMap( layerInstance -> layerInstance.visibleObjects( Visibility.application ) ); + } + + Stream<? extends TransientDescriptor> visibleTransients() + { + return usedLayerInstances.stream() + .flatMap( layerInstance -> layerInstance.visibleTransients( Visibility.application ) ); + } + + Stream<? extends EntityDescriptor> visibleEntities() + { + return usedLayerInstances.stream() + .flatMap( layerInstance -> layerInstance.visibleEntities( Visibility.application ) ); + } + + Stream<? extends ValueDescriptor> visibleValues() + { + return usedLayerInstances.stream() + .flatMap( layerInstance -> layerInstance.visibleValues( Visibility.application ) ); + } + + Stream<? extends ModelDescriptor> visibleServices() + { + return usedLayerInstances.stream() + .flatMap( layerInstance -> layerInstance.visibleServices( Visibility.application ) ); + } +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersModel.java new file mode 100644 index 0000000..c5bcd1a --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/UsedLayersModel.java @@ -0,0 +1,71 @@ +/* + * 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.polygene.runtime.structure; + +import java.util.List; +import java.util.stream.Stream; +import org.apache.polygene.api.structure.LayerDescriptor; +import org.apache.polygene.api.structure.UsedLayersDescriptor; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.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 Stream<? extends LayerDescriptor> layers() + { + return usedLayers.stream(); + } + + @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<LayerDescriptor> usedLayerInstances ) + { + return new UsedLayersInstance( usedLayerInstances ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/Visibilitypredicate.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/Visibilitypredicate.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/Visibilitypredicate.java new file mode 100644 index 0000000..df013c3 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/Visibilitypredicate.java @@ -0,0 +1,48 @@ +/* + * 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.polygene.runtime.structure; + +import java.util.function.Predicate; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.composite.ModelDescriptor; + +/** + * TODO + */ +public class Visibilitypredicate + implements Predicate<ModelDescriptor> +{ + public static final Predicate<ModelDescriptor> MODULE = new Visibilitypredicate( Visibility.module ); + public static final Predicate<ModelDescriptor> LAYER = new Visibilitypredicate( Visibility.layer ); + public static final Predicate<ModelDescriptor> APPLICATION = new Visibilitypredicate( Visibility.application ); + + private final Visibility visibility; + + public Visibilitypredicate( Visibility visibility ) + { + this.visibility = visibility; + } + + @Override + public boolean test( ModelDescriptor item ) + { + return item.visibility().ordinal() >= visibility.ordinal(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/types/ValueTypeFactory.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/types/ValueTypeFactory.java b/core/runtime/src/main/java/org/apache/polygene/runtime/types/ValueTypeFactory.java new file mode 100644 index 0000000..a3538c0 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/types/ValueTypeFactory.java @@ -0,0 +1,233 @@ +/* + * 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.polygene.runtime.types; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import org.apache.polygene.api.common.InvalidApplicationException; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.type.CollectionType; +import org.apache.polygene.api.type.EnumType; +import org.apache.polygene.api.type.MapType; +import org.apache.polygene.api.type.Serialization; +import org.apache.polygene.api.type.ValueCompositeType; +import org.apache.polygene.api.type.ValueType; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.api.util.HierarchicalVisitorAdapter; +import org.apache.polygene.api.value.ValueComposite; +import org.apache.polygene.runtime.association.AssociationsModel; +import org.apache.polygene.runtime.association.ManyAssociationsModel; +import org.apache.polygene.runtime.association.NamedAssociationsModel; +import org.apache.polygene.runtime.composite.CompositeMethodsModel; +import org.apache.polygene.runtime.composite.MixinsModel; +import org.apache.polygene.runtime.property.PropertiesModel; +import org.apache.polygene.runtime.structure.LayerModel; +import org.apache.polygene.runtime.structure.ModuleModel; +import org.apache.polygene.runtime.structure.UsedLayersModel; +import org.apache.polygene.runtime.value.ValueModel; +import org.apache.polygene.runtime.value.ValueStateModel; +import org.apache.polygene.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; + 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.apply( type ), collectedType ); + } + else + { + ValueType collectedType = newValueType( Object.class, declaringClass, compositeType, layer, module, variant ); + valueType = new CollectionType( Classes.RAW_CLASS.apply( 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.apply( 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.apply( 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.apply( type ) ).getFoundModel(); + + if( model == null ) + { + if( type.equals( ValueComposite.class ) ) + { + // Create default model + MixinsModel mixinsModel = new MixinsModel(); + List<Class<?>> valueComposite = new ArrayList<>(); + valueComposite.add( ValueComposite.class ); + ValueStateModel valueStateModel = new ValueStateModel( new PropertiesModel(), + new AssociationsModel(), + new ManyAssociationsModel(), + new NamedAssociationsModel() ); + model = new ValueModel( module, 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.apply( type ) ); + } + else + { + valueType = new ValueType( Classes.RAW_CLASS.apply( 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 = valueModel.types().anyMatch( t -> t.equals( 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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderEntityState.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderEntityState.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderEntityState.java new file mode 100644 index 0000000..c8739c5 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderEntityState.java @@ -0,0 +1,173 @@ +/* + * 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.polygene.runtime.unitofwork; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import org.apache.polygene.api.common.QualifiedName; +import org.apache.polygene.api.entity.EntityDescriptor; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.util.Classes; +import org.apache.polygene.spi.entity.EntityState; +import org.apache.polygene.spi.entity.EntityStatus; +import org.apache.polygene.spi.entity.ManyAssociationState; +import org.apache.polygene.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 entityReference() + { + return reference; + } + + @Override + public String version() + { + return ""; + } + + @Override + public Instant lastModified() + { + return Instant.MIN; + } + + @Override + public void remove() + { + } + + @Override + public EntityStatus status() + { + return EntityStatus.NEW; + } + + @Override + public boolean isAssignableTo( Class<?> type ) + { + return Classes.exactTypeSpecification( type ).test( 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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderManyAssociationState.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderManyAssociationState.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderManyAssociationState.java new file mode 100644 index 0000000..3b482d7 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderManyAssociationState.java @@ -0,0 +1,84 @@ +/* + * 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.polygene.runtime.unitofwork; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderNamedAssociationState.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderNamedAssociationState.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderNamedAssociationState.java new file mode 100644 index 0000000..d50d79b --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/BuilderNamedAssociationState.java @@ -0,0 +1,90 @@ +/* + * 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.polygene.runtime.unitofwork; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.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 LinkedHashMap<>(); + } + + @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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityBuilderInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityBuilderInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityBuilderInstance.java new file mode 100644 index 0000000..5044ff6 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityBuilderInstance.java @@ -0,0 +1,139 @@ +/* + * 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.polygene.runtime.unitofwork; + +import org.apache.polygene.api.entity.EntityBuilder; +import org.apache.polygene.api.entity.EntityDescriptor; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.entity.LifecycleException; +import org.apache.polygene.api.identity.Identity; +import org.apache.polygene.runtime.composite.FunctionStateResolver; +import org.apache.polygene.runtime.entity.EntityInstance; +import org.apache.polygene.runtime.entity.EntityModel; +import org.apache.polygene.spi.entity.EntityState; +import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork; +import org.apache.polygene.spi.module.ModuleSpi; + +import static org.apache.polygene.api.identity.HasIdentity.IDENTITY_STATE_NAME; + +/** + * 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 final EntityModel model; + private final ModuleUnitOfWork uow; + private final EntityStoreUnitOfWork store; + private Identity identity; + + private final BuilderEntityState entityState; + private final EntityInstance prototypeInstance; + + public EntityBuilderInstance( + EntityDescriptor model, + ModuleUnitOfWork uow, + EntityStoreUnitOfWork store, + Identity identity + ) + { + this( model, uow, store, identity, null ); + } + + public EntityBuilderInstance( + EntityDescriptor model, + ModuleUnitOfWork uow, + EntityStoreUnitOfWork store, + Identity identity, + FunctionStateResolver stateResolver + ) + { + this.model = (EntityModel) model; + this.uow = uow; + this.store = store; + this.identity = identity; + EntityReference reference = EntityReference.create( identity ); + entityState = new BuilderEntityState( model, reference ); + this.model.initState( model.module(), entityState ); + if( stateResolver != null ) + { + stateResolver.populateState( this.model, entityState ); + } + entityState.setPropertyValue( IDENTITY_STATE_NAME, identity ); + prototypeInstance = this.model.newInstance( uow, (ModuleSpi) model.module().instance(), 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(); + + // Figure out whether to use given or generated reference + Identity identity = (Identity) entityState.propertyValueOf( IDENTITY_STATE_NAME ); + EntityReference entityReference = EntityReference.create( identity ); + EntityState newEntityState = model.newEntityState( store, entityReference ); + + prototypeInstance.invokeCreate(); + + // Check constraints + prototypeInstance.checkConstraints(); + + entityState.copyTo( newEntityState ); + + EntityInstance instance = model.newInstance( uow, (ModuleSpi) model.module().instance(), 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/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityFunction.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityFunction.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityFunction.java new file mode 100644 index 0000000..936f5aa --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityFunction.java @@ -0,0 +1,46 @@ +/* + * 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.polygene.runtime.unitofwork; + +import java.lang.reflect.Type; +import java.util.function.BiFunction; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.unitofwork.UnitOfWorkFactory; + +import static org.apache.polygene.api.util.Classes.RAW_CLASS; + +public class EntityFunction + implements BiFunction<EntityReference, Type, Object> +{ + + private final UnitOfWorkFactory uowf; + + public EntityFunction( UnitOfWorkFactory uowf ) + { + this.uowf = uowf; + } + + @Override + public Object apply( EntityReference entityReference, Type type ) + { + return uowf.currentUnitOfWork().get( RAW_CLASS.apply( type ), entityReference.identity() ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityStateStore.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityStateStore.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityStateStore.java new file mode 100644 index 0000000..9f020f9 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/EntityStateStore.java @@ -0,0 +1,39 @@ +/* + * 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.polygene.runtime.unitofwork; + +import org.apache.polygene.api.association.AssociationStateHolder; +import org.apache.polygene.spi.entity.EntityState; + +/** + * JAVADOC + */ +final class EntityStateStore +{ + AssociationStateHolder stateHolder; + EntityState state; + + @Override + public String toString() + { + return state.entityReference().toString(); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/ModuleUnitOfWork.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/ModuleUnitOfWork.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/ModuleUnitOfWork.java new file mode 100644 index 0000000..bd18383 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/ModuleUnitOfWork.java @@ -0,0 +1,769 @@ +/* + * 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.polygene.runtime.unitofwork; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.polygene.api.PolygeneAPI; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.association.AssociationStateHolder; +import org.apache.polygene.api.association.ManyAssociation; +import org.apache.polygene.api.association.NamedAssociation; +import org.apache.polygene.api.composite.Composite; +import org.apache.polygene.api.entity.EntityBuilder; +import org.apache.polygene.api.entity.EntityComposite; +import org.apache.polygene.api.entity.EntityDescriptor; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.entity.LifecycleException; +import org.apache.polygene.api.identity.HasIdentity; +import org.apache.polygene.api.identity.Identity; +import org.apache.polygene.api.identity.IdentityGenerator; +import org.apache.polygene.api.identity.StringIdentity; +import org.apache.polygene.api.injection.scope.Service; +import org.apache.polygene.api.injection.scope.Structure; +import org.apache.polygene.api.injection.scope.Uses; +import org.apache.polygene.api.property.Property; +import org.apache.polygene.api.property.PropertyDescriptor; +import org.apache.polygene.api.property.StateHolder; +import org.apache.polygene.api.query.Query; +import org.apache.polygene.api.query.QueryBuilder; +import org.apache.polygene.api.query.QueryExecutionException; +import org.apache.polygene.api.query.grammar.OrderBy; +import org.apache.polygene.api.service.NoSuchServiceException; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.unitofwork.ConcurrentEntityModificationException; +import org.apache.polygene.api.unitofwork.NoSuchEntityException; +import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException; +import org.apache.polygene.api.unitofwork.UnitOfWork; +import org.apache.polygene.api.unitofwork.UnitOfWorkCallback; +import org.apache.polygene.api.unitofwork.UnitOfWorkCompletionException; +import org.apache.polygene.api.unitofwork.UnitOfWorkFactory; +import org.apache.polygene.api.usecase.Usecase; +import org.apache.polygene.api.util.NullArgumentException; +import org.apache.polygene.api.value.ValueBuilder; +import org.apache.polygene.api.value.ValueComposite; +import org.apache.polygene.runtime.association.AssociationInstance; +import org.apache.polygene.runtime.composite.FunctionStateResolver; +import org.apache.polygene.runtime.entity.EntityInstance; +import org.apache.polygene.runtime.entity.EntityModel; +import org.apache.polygene.runtime.property.PropertyModel; +import org.apache.polygene.runtime.value.ValueInstance; +import org.apache.polygene.spi.entity.EntityState; +import org.apache.polygene.spi.entity.EntityStatus; +import org.apache.polygene.spi.entitystore.EntityStore; +import org.apache.polygene.spi.module.ModuleSpi; +import org.apache.polygene.spi.query.EntityFinder; +import org.apache.polygene.spi.query.EntityFinderException; +import org.apache.polygene.spi.query.QueryBuilderSPI; +import org.apache.polygene.spi.query.QuerySource; + +import static org.apache.polygene.api.identity.HasIdentity.IDENTITY_STATE_NAME; + +/** + * JAVADOC + */ +public class ModuleUnitOfWork + implements UnitOfWork +{ + @Uses + private UnitOfWorkInstance uow; + + @Structure + private PolygeneAPI api; + + @Structure + private ModuleDescriptor module; + + @Service + private UnitOfWorkFactory unitOfWorkFactory; + + public ModuleDescriptor module() + { + return module; + } + + public UnitOfWorkInstance instance() + { + return uow; + } + + @Override + public UnitOfWorkFactory unitOfWorkFactory() + { + return unitOfWorkFactory; + } + + @Override + public Instant 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 NoSuchEntityTypeException, LifecycleException + { + return newEntity( type, null ); + } + + @Override + public <T> T newEntity( Class<T> type, Identity identity ) + throws NoSuchEntityTypeException, LifecycleException + { + return newEntityBuilder( type, identity ).newInstance(); + } + + @Override + public <T> EntityBuilder<T> newEntityBuilder( Class<T> type ) + throws NoSuchEntityTypeException + { + return newEntityBuilder( type, null ); + } + + @Override + public <T> EntityBuilder<T> newEntityBuilder( Class<T> type, Identity identity ) + throws NoSuchEntityTypeException + { + EntityDescriptor model = module.typeLookup().lookupEntityModel( type ); + + if( model == null ) + { + throw new NoSuchEntityTypeException( type.getName(), module.name(), module.typeLookup() ); + } + + ModuleDescriptor modelModule = model.module(); + EntityStore entityStore = ( (ModuleSpi) modelModule.instance() ).entityStore(); + + // Generate id if necessary + if( identity == null ) + { + IdentityGenerator idGen = ( (ModuleSpi) modelModule.instance() ).identityGenerator(); + if( idGen == null ) + { + throw new NoSuchServiceException( IdentityGenerator.class.getName(), modelModule + .name(), modelModule.typeLookup() ); + } + identity = idGen.generate( model.types().findFirst().orElse( null ) ); + } + EntityBuilder<T> builder; + + builder = new EntityBuilderInstance<>( model, + this, + uow.getEntityStoreUnitOfWork( entityStore ), + identity ); + return builder; + } + + @Override + public <T> EntityBuilder<T> newEntityBuilderWithState( + Class<T> type, + Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Stream<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction + ) + throws NoSuchEntityTypeException + { + return newEntityBuilderWithState( type, null, + propertyFunction, + associationFunction, + manyAssociationFunction, + namedAssociationFunction ); + } + + @Override + public <T> EntityBuilder<T> newEntityBuilderWithState( + Class<T> type, Identity identity, + Function<PropertyDescriptor, Object> propertyFunction, + Function<AssociationDescriptor, EntityReference> associationFunction, + Function<AssociationDescriptor, Stream<EntityReference>> manyAssociationFunction, + Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction + ) + throws NoSuchEntityTypeException + { + NullArgumentException.validateNotNull( "propertyFunction", propertyFunction ); + NullArgumentException.validateNotNull( "associationFunction", associationFunction ); + NullArgumentException.validateNotNull( "manyAssociationFunction", manyAssociationFunction ); + NullArgumentException.validateNotNull( "namedAssociationFunction", namedAssociationFunction ); + + EntityDescriptor model = module.typeLookup().lookupEntityModel( type ); + + if( model == null ) + { + throw new NoSuchEntityTypeException( type.getName(), module.name(), module.typeLookup() ); + } + + ModuleDescriptor modelModule = model.module(); + ModuleSpi moduleSpi = (ModuleSpi) modelModule.instance(); + EntityStore entityStore = moduleSpi.entityStore(); + + FunctionStateResolver stateResolver = new FunctionStateResolver( + propertyFunction, associationFunction, manyAssociationFunction, namedAssociationFunction + ); + + if( identity == null ) + { + // Use reference from StateResolver if available + PropertyModel identityModel = (PropertyModel) model + .state() + .findPropertyModelByQualifiedName( IDENTITY_STATE_NAME ); + String propertyState = (String) stateResolver.getPropertyState(identityModel); + if( propertyState == null ) + { + // Generate reference + IdentityGenerator idGen = moduleSpi.identityGenerator(); + if( idGen == null ) + { + String typeName = IdentityGenerator.class.getName(); + throw new NoSuchServiceException( typeName, modelModule.name(), modelModule.typeLookup() ); + } + identity = idGen.generate( model.types().findFirst().orElse( null ) ); + } + else + { + identity = new StringIdentity(propertyState); + } + } + + return new EntityBuilderInstance<>( model, + this, + uow.getEntityStoreUnitOfWork( entityStore ), + identity, + stateResolver ); + } + + @Override + public <T> T get( Class<T> type, Identity identity ) + throws NoSuchEntityTypeException, NoSuchEntityException + { + Iterable<EntityDescriptor> models = module.typeLookup().lookupEntityModels( type ); + + if( !models.iterator().hasNext() ) + { + throw new NoSuchEntityTypeException( type.getName(), module.name(), module.typeLookup() ); + } + + return uow.get( EntityReference.create( identity ), this, models, type ); + } + + @Override + @SuppressWarnings( "unchecked" ) + public <T> T get( T entity ) + throws NoSuchEntityTypeException + { + EntityComposite entityComposite = (EntityComposite) entity; + EntityInstance compositeInstance = EntityInstance.entityInstanceOf( entityComposite ); + EntityDescriptor model = compositeInstance.entityModel(); + Class<T> type = (Class<T>) compositeInstance.types().findFirst().orElse( null ); + return uow.get( compositeInstance.reference(), 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.reference() ); + } + else if( compositeInstance.status() == EntityStatus.LOADED || compositeInstance.status() == EntityStatus.UPDATED ) + { + compositeInstance.remove( this ); + } + else + { + throw new NoSuchEntityException( compositeInstance.reference(), 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 HasIdentity> T toValue(Class<T> primaryType, T entityComposite ) + { + Function<PropertyDescriptor, Object> propertyFunction = new ToValuePropertyMappingFunction( entityComposite ); + Function<AssociationDescriptor, EntityReference> assocationFunction = new ToValueAssociationMappingFunction<>( entityComposite ); + Function<AssociationDescriptor, Stream<EntityReference>> manyAssocFunction = new ToValueManyAssociationMappingFunction<>( entityComposite ); + Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssocFunction = new ToValueNameAssociationMappingFunction<>( entityComposite ); + + @SuppressWarnings( "unchecked" ) + ValueBuilder<T> builder = module().instance().newValueBuilderWithState( + primaryType, propertyFunction, assocationFunction, manyAssocFunction, namedAssocFunction ); + return builder.newInstance(); + } + + @Override + public <T extends HasIdentity> Map<String, T> toValueMap(NamedAssociation<T> association ) + { + @SuppressWarnings( "unchecked" ) + Class<T> primaryType = (Class<T>) api.associationDescriptorFor( association ).type(); + + return association + .toMap() + .entrySet() + .stream() + .collect( Collectors.toMap( Map.Entry::getKey, entry -> toValue( primaryType, entry.getValue()) ) ); + } + + @Override + public <T extends HasIdentity> List<T> toValueList(ManyAssociation<T> association ) + { + @SuppressWarnings( "unchecked" ) + Class<T> primaryType = (Class<T>) api.associationDescriptorFor( association ).type(); + + return association + .toList() + .stream() + .map( entity -> toValue( primaryType, entity ) ) + .collect( Collectors.toList() ); + } + + @Override + public <T extends HasIdentity> Set<T> toValueSet(ManyAssociation<T> association ) + { + @SuppressWarnings( "unchecked" ) + Class<T> primaryType = (Class<T>) api.associationDescriptorFor( association ).type(); + + return association + .toSet() + .stream() + .map( entity -> toValue( primaryType, entity ) ) + .collect( Collectors.toSet() ); + } + + @Override + public <T extends HasIdentity> T toEntity(Class<T> primaryType, T valueComposite ) + { + Function<PropertyDescriptor, Object> propertyFunction = new ToEntityPropertyMappingFunction<>( valueComposite ); + Function<AssociationDescriptor, EntityReference> assocationFunction = new ToEntityAssociationMappingFunction<>( valueComposite ); + Function<AssociationDescriptor, Stream<EntityReference>> manyAssocFunction = new ToEntityManyAssociationMappingFunction<>( valueComposite ); + Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssocFunction = new ToEntityNameAssociationMappingFunction<>( valueComposite ); + + try + { + T entity = get( primaryType, valueComposite.identity().get() ); + // 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, + valueComposite.identity().get(), + 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, + Predicate<Composite> whereClause, + List<OrderBy> orderBySegments, + Integer firstResult, + Integer maxResults, + Map<String, Object> variables + ) + { + final EntityFinder entityFinder = moduleUnitOfWork.module() + .instance() + .findService( EntityFinder.class ) + .get(); + + try + { + EntityReference foundEntity = entityFinder.findEntity( resultType, whereClause, variables == null ? Collections.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, + Predicate<Composite> whereClause, + List<OrderBy> orderBySegments, + Integer firstResult, + Integer maxResults, + Map<String, Object> variables + ) + { + EntityFinder entityFinder = moduleUnitOfWork.module().instance().findService( EntityFinder.class ).get(); + + try + { + return entityFinder.countEntities( resultType, whereClause, variables == null ? Collections.emptyMap() : variables ); + } + catch( EntityFinderException e ) + { + e.printStackTrace(); + return 0; + } + } + + @Override + public <T> Stream<T> stream( Class<T> resultType, + Predicate<Composite> whereClause, + List<OrderBy> orderBySegments, + Integer firstResult, + Integer maxResults, + Map<String, Object> variables ) + { + EntityFinder entityFinder = moduleUnitOfWork.module().instance().findService( EntityFinder.class ).get(); + + try + { + return entityFinder.findEntities( + resultType, + whereClause, + orderBySegments, + firstResult, + maxResults, + variables == null ? Collections.emptyMap() : variables + ).map( ref -> + { + try + { + return moduleUnitOfWork.get( resultType, ref.identity() ); + } + catch( NoSuchEntityException e ) + { + // Index is out of sync - entity has been removed + return null; + } + } ); + } + catch( EntityFinderException e ) + { + throw new QueryExecutionException( "Query '" + toString() + "' could not be executed", e ); + } + } + } + + private class ToValuePropertyMappingFunction + implements Function<PropertyDescriptor, Object> + { + private Object entity; + + public ToValuePropertyMappingFunction( Object entity ) + { + this.entity = entity; + } + + @Override + public Object apply( 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 apply( AssociationDescriptor associationDescriptor ) + { + EntityState entityState = EntityInstance.entityInstanceOf( (EntityComposite) entity ).entityState(); + return entityState.associationValueOf( associationDescriptor.qualifiedName() ); + } + } + + private class ToValueManyAssociationMappingFunction<T> + implements Function<AssociationDescriptor, Stream<EntityReference>> + { + private final T entity; + + public ToValueManyAssociationMappingFunction( T entity ) + { + this.entity = entity; + } + + @Override + public Stream<EntityReference> apply( AssociationDescriptor associationDescriptor ) + { + EntityState entityState = EntityInstance.entityInstanceOf( (EntityComposite) entity ).entityState(); + return entityState.manyAssociationValueOf( associationDescriptor.qualifiedName() ).stream(); + } + } + + private class ToValueNameAssociationMappingFunction<T> + implements Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> + { + private final T entity; + + public ToValueNameAssociationMappingFunction( T entity ) + { + this.entity = entity; + } + + @Override + public Stream<Map.Entry<String, EntityReference>> apply( AssociationDescriptor associationDescriptor ) + { + EntityState entityState = EntityInstance.entityInstanceOf( (EntityComposite) entity ).entityState(); + return entityState.namedAssociationValueOf( associationDescriptor.qualifiedName() ).stream(); + } + } + + private class ToEntityPropertyMappingFunction<T> + implements Function<PropertyDescriptor, Object> + { + private final T value; + + public ToEntityPropertyMappingFunction( T value ) + { + this.value = value; + } + + @Override + public Object apply( 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 apply( 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, Stream<EntityReference>> + { + + private final T value; + + public ToEntityManyAssociationMappingFunction( T valueComposite ) + { + this.value = valueComposite; + } + + @Override + public Stream<EntityReference> apply( AssociationDescriptor associationDescriptor ) + { + ValueInstance valueInstance = ValueInstance.valueInstanceOf( (ValueComposite) value ); + return valueInstance.state().manyAssociationFor( associationDescriptor.accessor() ).references(); + } + } + + private class ToEntityNameAssociationMappingFunction<T> + implements Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> + { + private final T value; + + public ToEntityNameAssociationMappingFunction( T valueComposite ) + { + this.value = valueComposite; + } + + @Override + public Stream<Map.Entry<String, EntityReference>> apply( AssociationDescriptor associationDescriptor ) + { + ValueInstance valueInstance = ValueInstance.valueInstanceOf( (ValueComposite) value ); + return valueInstance.state().namedAssociationFor( associationDescriptor.accessor() ).references(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkFactoryMixin.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkFactoryMixin.java b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkFactoryMixin.java new file mode 100644 index 0000000..d5a3609 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/unitofwork/UnitOfWorkFactoryMixin.java @@ -0,0 +1,100 @@ +/* + * 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.polygene.runtime.unitofwork; + +import java.time.Instant; +import java.util.Stack; +import org.apache.polygene.api.composite.TransientBuilderFactory; +import org.apache.polygene.api.entity.EntityComposite; +import org.apache.polygene.api.injection.scope.Structure; +import org.apache.polygene.api.metrics.MetricsProvider; +import org.apache.polygene.api.time.SystemTime; +import org.apache.polygene.api.unitofwork.UnitOfWork; +import org.apache.polygene.api.unitofwork.UnitOfWorkFactory; +import org.apache.polygene.api.usecase.Usecase; +import org.apache.polygene.runtime.entity.EntityInstance; +import org.apache.polygene.spi.module.ModuleSpi; + +public class UnitOfWorkFactoryMixin + implements UnitOfWorkFactory +{ + @Structure + private TransientBuilderFactory tbf; + + @Structure + private ModuleSpi module; + + // Implementation of UnitOfWorkFactory + @Override + public UnitOfWork newUnitOfWork() + { + return newUnitOfWork( Usecase.DEFAULT ); + } + + @Override + public UnitOfWork newUnitOfWork(Instant currentTime ) + { + return newUnitOfWork( Usecase.DEFAULT, currentTime ); + } + + @Override + public UnitOfWork newUnitOfWork( Usecase usecase ) + { + return newUnitOfWork( usecase == null ? Usecase.DEFAULT : usecase, SystemTime.now() ); + } + + @Override + public UnitOfWork newUnitOfWork( Usecase usecase, Instant currentTime ) + { + UnitOfWorkInstance unitOfWorkInstance = new UnitOfWorkInstance( module, usecase, currentTime, metricsProvider() ); + return tbf.newTransient( UnitOfWork.class, unitOfWorkInstance ); + } + + private MetricsProvider metricsProvider() + { + return module.metricsProvider(); + } + + @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 tbf.newTransient( UnitOfWork.class, stack.peek() ); + } + + @Override + public UnitOfWork getUnitOfWork( EntityComposite entity ) + { + EntityInstance instance = EntityInstance.entityInstanceOf( entity ); + return instance.unitOfWork(); + } +}
