blob: 969ae18001786b2b8db0a7e44482556a39a6624b [file] [log] [blame]
/*
* 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.lang.reflect.AccessibleObject;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import org.apache.polygene.api.activation.Activation;
import org.apache.polygene.api.activation.ActivationEventListener;
import org.apache.polygene.api.activation.ActivationException;
import org.apache.polygene.api.activation.PassivationException;
import org.apache.polygene.api.association.AssociationDescriptor;
import org.apache.polygene.api.common.ConstructionException;
import org.apache.polygene.api.composite.Composite;
import org.apache.polygene.api.composite.ModelDescriptor;
import org.apache.polygene.api.composite.NoSuchTransientTypeException;
import org.apache.polygene.api.composite.TransientBuilder;
import org.apache.polygene.api.composite.TransientBuilderFactory;
import org.apache.polygene.api.composite.TransientDescriptor;
import org.apache.polygene.api.entity.EntityReference;
import org.apache.polygene.api.identity.IdentityGenerator;
import org.apache.polygene.api.metrics.MetricsProvider;
import org.apache.polygene.api.object.NoSuchObjectTypeException;
import org.apache.polygene.api.object.ObjectDescriptor;
import org.apache.polygene.api.object.ObjectFactory;
import org.apache.polygene.api.property.Property;
import org.apache.polygene.api.property.PropertyDescriptor;
import org.apache.polygene.api.query.QueryBuilder;
import org.apache.polygene.api.query.QueryBuilderFactory;
import org.apache.polygene.api.serialization.Serialization;
import org.apache.polygene.api.serialization.SerializationException;
import org.apache.polygene.api.service.NoSuchServiceTypeException;
import org.apache.polygene.api.service.ServiceFinder;
import org.apache.polygene.api.service.ServiceReference;
import org.apache.polygene.api.structure.LayerDescriptor;
import org.apache.polygene.api.structure.Module;
import org.apache.polygene.api.structure.ModuleDescriptor;
import org.apache.polygene.api.structure.TypeLookup;
import org.apache.polygene.api.type.HasTypes;
import org.apache.polygene.api.unitofwork.UnitOfWorkException;
import org.apache.polygene.api.unitofwork.UnitOfWorkFactory;
import org.apache.polygene.api.value.NoSuchValueTypeException;
import org.apache.polygene.api.value.ValueBuilder;
import org.apache.polygene.api.value.ValueBuilderFactory;
import org.apache.polygene.api.value.ValueDescriptor;
import org.apache.polygene.runtime.activation.ActivationDelegate;
import org.apache.polygene.runtime.composite.FunctionStateResolver;
import org.apache.polygene.runtime.composite.StateResolver;
import org.apache.polygene.runtime.composite.TransientBuilderInstance;
import org.apache.polygene.runtime.composite.TransientStateInstance;
import org.apache.polygene.runtime.composite.UsesInstance;
import org.apache.polygene.runtime.injection.InjectionContext;
import org.apache.polygene.runtime.object.ObjectModel;
import org.apache.polygene.runtime.property.PropertyInstance;
import org.apache.polygene.runtime.property.PropertyModel;
import org.apache.polygene.runtime.query.QueryBuilderFactoryImpl;
import org.apache.polygene.runtime.service.ImportedServicesInstance;
import org.apache.polygene.runtime.service.ImportedServicesModel;
import org.apache.polygene.runtime.service.ServicesInstance;
import org.apache.polygene.runtime.service.ServicesModel;
import org.apache.polygene.runtime.type.ValueTypeFactoryInstance;
import org.apache.polygene.runtime.value.ValueBuilderInstance;
import org.apache.polygene.runtime.value.ValueBuilderWithPrototype;
import org.apache.polygene.runtime.value.ValueBuilderWithState;
import org.apache.polygene.runtime.value.ValueInstance;
import org.apache.polygene.spi.entitystore.EntityStore;
import org.apache.polygene.spi.module.ModuleSpi;
import static java.util.Arrays.asList;
import static java.util.stream.Stream.concat;
import static org.apache.polygene.api.composite.CompositeInstance.compositeInstanceOf;
/**
* Instance of a Polygene Module. Contains the various composites for this Module.
*/
public class ModuleInstance
implements Module, ModuleSpi, Activation
{
// Constructor parameters
private final ModuleModel model;
private final LayerDescriptor layer;
private final TypeLookup typeLookup;
private final ServicesInstance services;
private final ImportedServicesInstance importedServices;
// Eager instance objects
private final ActivationDelegate activation;
private final QueryBuilderFactory queryBuilderFactory;
// Lazy assigned on accessors
private EntityStore store;
private IdentityGenerator generator;
private Serialization serialization;
private MetricsProvider metrics;
private UnitOfWorkFactory uowf;
@SuppressWarnings( "LeakingThisInConstructor" )
ModuleInstance( ModuleModel moduleModel, LayerDescriptor layer, TypeLookup typeLookup,
ServicesModel servicesModel, ImportedServicesModel importedServicesModel
)
{
// Constructor parameters
model = moduleModel;
this.layer = layer;
this.typeLookup = typeLookup;
services = servicesModel.newInstance( moduleModel );
importedServices = importedServicesModel.newInstance( moduleModel );
// Eager instance objects
activation = new ActivationDelegate( this );
queryBuilderFactory = new QueryBuilderFactoryImpl( this );
// Activation
services.registerActivationEventListener( activation );
importedServices.registerActivationEventListener( activation );
}
@Override
public String toString()
{
return model.toString();
}
@Override
public ModuleDescriptor descriptor()
{
return model;
}
// Implementation of Module
@Override
public String name()
{
return model.name();
}
// 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 NoSuchObjectTypeException
{
Objects.requireNonNull( mixinType, "mixinType" );
ObjectDescriptor model = typeLookup.lookupObjectModel( mixinType );
if( model == null )
{
throw new NoSuchObjectTypeException( mixinType.getName(), name(),
typeLookup.allObjects().flatMap( HasTypes::types ) );
}
InjectionContext injectionContext = new InjectionContext( model.module(), UsesInstance.EMPTY_USES.use( uses ) );
return mixinType.cast( ( (ObjectModel) model ).newInstance( injectionContext ) );
}
@Override
public void injectTo( Object instance, Object... uses )
throws ConstructionException
{
Objects.requireNonNull( instance, "instance" );
ObjectDescriptor model = typeLookup.lookupObjectModel( instance.getClass() );
if( model == null )
{
throw new NoSuchObjectTypeException( instance.getClass().getName(), name(),
typeLookup.allObjects().flatMap( HasTypes::types ) );
}
InjectionContext injectionContext = new InjectionContext( model.module(), UsesInstance.EMPTY_USES.use( uses ) );
( (ObjectModel) model ).inject( injectionContext, instance );
}
// Implementation of TransientBuilderFactory
@Override
public <T> TransientBuilder<T> newTransientBuilder( Class<T> mixinType )
throws NoSuchTransientTypeException
{
Objects.requireNonNull( mixinType, "mixinType" );
TransientDescriptor model = typeLookup.lookupTransientModel( mixinType );
if( model == null )
{
throw new NoSuchTransientTypeException( mixinType.getName(), descriptor() );
}
Map<AccessibleObject, Property<?>> properties = new HashMap<>();
model.state().properties().forEach(
propertyModel ->
{
Object initialValue = propertyModel.resolveInitialValue( model.module() );
Property<?> property = new PropertyInstance<>( ( (PropertyModel) propertyModel ).getBuilderInfo(),
initialValue );
properties.put( propertyModel.accessor(), property );
} );
TransientStateInstance state = new TransientStateInstance( properties );
return new TransientBuilderInstance<>( model, state, UsesInstance.EMPTY_USES );
}
@Override
public <T> T newTransient( final Class<T> mixinType, Object... uses )
throws NoSuchTransientTypeException, ConstructionException
{
return newTransientBuilder( mixinType ).use( uses ).newInstance();
}
// Implementation of ValueBuilderFactory
@Override
public <T> T newValue( Class<T> mixinType )
throws NoSuchValueTypeException, ConstructionException
{
return newValueBuilder( mixinType ).newInstance();
}
@Override
public <T> ValueBuilder<T> newValueBuilder( Class<T> mixinType )
throws NoSuchValueTypeException
{
Objects.requireNonNull( mixinType, "mixinType" );
ValueDescriptor compositeModelModule = typeLookup.lookupValueModel( mixinType );
if( compositeModelModule == null )
{
throw new NoSuchValueTypeException( mixinType.getName(), descriptor() );
}
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, Stream<EntityReference>> manyAssociationFunction,
Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction
)
{
Objects.requireNonNull( propertyFunction, "propertyFunction" );
Objects.requireNonNull( associationFunction, "associationFunction" );
Objects.requireNonNull( manyAssociationFunction, "manyAssociationFunction" );
Objects.requireNonNull( namedAssociationFunction, "namedAssociationFunction" );
ValueDescriptor compositeModelModule = typeLookup.lookupValueModel( mixinType );
if( compositeModelModule == null )
{
throw new NoSuchValueTypeException( mixinType.getName(), descriptor() );
}
StateResolver stateResolver = new FunctionStateResolver(
propertyFunction, associationFunction, manyAssociationFunction, namedAssociationFunction
);
return new ValueBuilderWithState<>( compositeModelModule, this, stateResolver );
}
private static class InitialStateResolver
implements StateResolver
{
private final ModuleDescriptor module;
private InitialStateResolver( ModuleDescriptor module )
{
this.module = module;
}
@Override
public Object getPropertyState( PropertyDescriptor propertyDescriptor )
{
return propertyDescriptor.resolveInitialValue( module );
}
@Override
public EntityReference getAssociationState( AssociationDescriptor associationDescriptor )
{
return null;
}
@Override
public Stream<EntityReference> getManyAssociationState( AssociationDescriptor associationDescriptor )
{
return new ArrayList<EntityReference>().stream();
}
@Override
public Stream<Map.Entry<String, EntityReference>>
getNamedAssociationState( AssociationDescriptor associationDescriptor )
{
return new HashMap<String, EntityReference>().entrySet().stream();
}
}
@Override
@SuppressWarnings( "unchecked" )
public <T> ValueBuilder<T> newValueBuilderWithPrototype( T prototype )
{
Objects.requireNonNull( prototype, "prototype" );
ValueInstance valueInstance = (ValueInstance) compositeInstanceOf( (Composite) prototype );
Class<Composite> valueType = (Class<Composite>) valueInstance.types().findFirst().orElse( null );
ValueDescriptor model = typeLookup.lookupValueModel( valueType );
if( model == null )
{
throw new NoSuchValueTypeException( valueType.getName(), descriptor() );
}
return new ValueBuilderWithPrototype<>( model, this, prototype );
}
@Override
public <T> T newValueFromSerializedState( Class<T> mixinType, String serializedState )
throws NoSuchValueTypeException, ConstructionException
{
Objects.requireNonNull( mixinType, "mixinType" );
ValueDescriptor model = typeLookup.lookupValueModel( mixinType );
if( model == null )
{
throw new NoSuchValueTypeException( mixinType.getName(), descriptor() );
}
try
{
return serialization().deserialize( model.module(), model.valueType(), serializedState );
}
catch( SerializationException ex )
{
throw new ConstructionException( "Could not create value from serialized state", ex );
}
}
// Implementation of QueryBuilderFactory
@Override
public <T> QueryBuilder<T> newQueryBuilder( final Class<T> resultType )
{
return queryBuilderFactory.newQueryBuilder( resultType );
}
@Override
public <T> ServiceReference<T> findService( Class<T> serviceType )
throws NoSuchServiceTypeException
{
return findService( (Type) serviceType );
}
@Override
public <T> ServiceReference<T> findService( Type serviceType )
{
ModelDescriptor serviceModel = typeLookup.lookupServiceModel( serviceType );
if( serviceModel == null )
{
throw new NoSuchServiceTypeException( serviceType.getTypeName(), descriptor() );
}
return findServiceReferenceInstance( serviceModel );
}
@Override
public <T> Stream<ServiceReference<T>> findServices( final Class<T> serviceType )
{
return findServices( (Type) serviceType );
}
@Override
@SuppressWarnings( "unchecked" )
public <T> Stream<ServiceReference<T>> findServices( final Type serviceType )
{
List<? extends ModelDescriptor> serviceModels = typeLookup.lookupServiceModels( serviceType );
if( serviceModels == null )
{
return Stream.empty();
}
//noinspection unchecked
return serviceModels.stream()
.map( this::findServiceReferenceInstance )
.filter( Objects::nonNull )
.filter( ref -> ref.hasType( serviceType ) )
.map( ref -> (ServiceReference<T>) ref );
}
@SuppressWarnings( "unchecked" )
private <T> ServiceReference<T> findServiceReferenceInstance( ModelDescriptor model )
{
ModuleInstance moduleInstanceOfModel = (ModuleInstance) model.module().instance();
Optional<ServiceReference<?>> candidate =
concat( moduleInstanceOfModel.services.references(), moduleInstanceOfModel.importedServices.references() )
.filter( ref -> ref.model().equals( model ) )
.findAny();
if( candidate.isPresent() )
{
ServiceReference<?> serviceReference = candidate.get();
return (ServiceReference<T>) serviceReference;
}
return null;
}
// Implementation of Activation
@Override
public void activate()
throws ActivationException
{
activation.activate( model.newActivatorsInstance(), asList( 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
ModuleModel model()
{
return model;
}
@Override
public LayerDescriptor layer()
{
return layer;
}
@Override
public TypeLookup typeLookup()
{
return typeLookup;
}
@Override
public EntityStore entityStore()
{
if( store == null )
{
synchronized( this )
{
if( store == null )
{
try
{
store = findService( EntityStore.class ).get();
}
catch( NoSuchServiceTypeException e )
{
throw new UnitOfWorkException( "No EntityStore service available in module " + name() );
}
}
}
}
return store;
}
@Override
public UnitOfWorkFactory unitOfWorkFactory()
{
if( uowf == null )
{
synchronized( this )
{
if( uowf == null )
{
try
{
uowf = findService( UnitOfWorkFactory.class ).get();
}
catch( NoSuchServiceTypeException e )
{
throw new UnitOfWorkException( "No UnitOfWorkFactory service available in module " + name() );
}
}
}
}
return uowf;
}
@Override
public ServiceFinder serviceFinder()
{
return this;
}
@Override
public ValueBuilderFactory valueBuilderFactory()
{
return this;
}
@Override
public TransientBuilderFactory transientBuilderFactory()
{
return this;
}
@Override
public ObjectFactory objectFactory()
{
return this;
}
@Override
public IdentityGenerator identityGenerator()
{
if( generator == null )
{
synchronized( this )
{
if( generator == null )
{
generator = findService( IdentityGenerator.class ).get();
}
}
}
return generator;
}
@Override
public Serialization serialization()
{
if( serialization == null )
{
synchronized( this )
{
if( serialization == null )
{
serialization = findService( Serialization.class ).get();
}
}
}
return serialization;
}
@Override
public MetricsProvider metricsProvider()
{
if( metrics == null )
{
synchronized( this )
{
if( metrics == null )
{
metrics = findService( MetricsProvider.class ).get();
}
}
}
return metrics;
}
@Override
public ValueTypeFactoryInstance valueTypeFactory()
{
return ValueTypeFactoryInstance.instance();
}
}