blob: 673dfe71e4a13179204007b3508120ce470e9588 [file] [log] [blame]
/*
* Copyright (c) 2007, 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.bootstrap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.zest.api.activation.Activator;
import org.apache.zest.api.common.MetaInfo;
import org.apache.zest.api.common.Visibility;
import org.apache.zest.api.composite.TransientComposite;
import org.apache.zest.api.entity.EntityComposite;
import org.apache.zest.api.entity.Identity;
import org.apache.zest.api.service.DuplicateServiceIdentityException;
import org.apache.zest.api.service.ServiceImporter;
import org.apache.zest.api.structure.Module;
import org.apache.zest.api.type.HasTypes;
import org.apache.zest.api.type.MatchTypeSpecification;
import org.apache.zest.api.value.ValueComposite;
import org.apache.zest.bootstrap.AssemblyException;
import org.apache.zest.bootstrap.AssemblySpecifications;
import org.apache.zest.bootstrap.AssemblyVisitor;
import org.apache.zest.bootstrap.ConfigurationDeclaration;
import org.apache.zest.bootstrap.EntityAssembly;
import org.apache.zest.bootstrap.EntityDeclaration;
import org.apache.zest.bootstrap.ImportedServiceAssembly;
import org.apache.zest.bootstrap.ImportedServiceDeclaration;
import org.apache.zest.bootstrap.LayerAssembly;
import org.apache.zest.bootstrap.MetaInfoDeclaration;
import org.apache.zest.bootstrap.MixinDeclaration;
import org.apache.zest.bootstrap.ModuleAssembly;
import org.apache.zest.bootstrap.ObjectAssembly;
import org.apache.zest.bootstrap.ObjectDeclaration;
import org.apache.zest.bootstrap.ServiceAssembly;
import org.apache.zest.bootstrap.ServiceDeclaration;
import org.apache.zest.bootstrap.TransientAssembly;
import org.apache.zest.bootstrap.TransientDeclaration;
import org.apache.zest.bootstrap.ValueAssembly;
import org.apache.zest.bootstrap.ValueDeclaration;
import org.apache.zest.functional.Iterables;
import org.apache.zest.functional.Specification;
import org.apache.zest.functional.Specifications;
import org.apache.zest.runtime.activation.ActivatorsModel;
import org.apache.zest.runtime.composite.TransientModel;
import org.apache.zest.runtime.composite.TransientsModel;
import org.apache.zest.runtime.entity.EntitiesModel;
import org.apache.zest.runtime.entity.EntityModel;
import org.apache.zest.runtime.object.ObjectModel;
import org.apache.zest.runtime.object.ObjectsModel;
import org.apache.zest.runtime.service.ImportedServiceModel;
import org.apache.zest.runtime.service.ImportedServicesModel;
import org.apache.zest.runtime.service.ServiceModel;
import org.apache.zest.runtime.service.ServicesModel;
import org.apache.zest.runtime.structure.ModuleModel;
import org.apache.zest.runtime.value.ValueModel;
import org.apache.zest.runtime.value.ValuesModel;
import static org.apache.zest.functional.Iterables.first;
import static org.apache.zest.functional.Iterables.iterable;
/**
* Assembly of a Module. This is where you register all objects, Composites,
* Services. Each "add" method returns a declaration that you can use to add
* additional information and metadata. If you call an "add" method with many
* parameters then the declared metadata will apply to all types in the method
* call.
*/
public final class ModuleAssemblyImpl
implements ModuleAssembly
{
private final LayerAssembly layerAssembly;
private String name;
private final MetaInfo metaInfo = new MetaInfo();
private final List<Class<? extends Activator<Module>>> activators = new ArrayList<>();
private final List<ServiceAssemblyImpl> serviceAssemblies = new ArrayList<>();
private final Map<Class<?>, ImportedServiceAssemblyImpl> importedServiceAssemblies = new LinkedHashMap<>();
private final Map<Class<? extends EntityComposite>, EntityAssemblyImpl> entityAssemblies = new LinkedHashMap<>();
private final Map<Class<? extends ValueComposite>, ValueAssemblyImpl> valueAssemblies = new LinkedHashMap<>();
private final Map<Class<? extends TransientComposite>, TransientAssemblyImpl> transientAssemblies = new LinkedHashMap<>();
private final Map<Class<?>, ObjectAssemblyImpl> objectAssemblies = new LinkedHashMap<>();
private final MetaInfoDeclaration metaInfoDeclaration = new MetaInfoDeclaration();
public ModuleAssemblyImpl( LayerAssembly layerAssembly, String name )
{
this.layerAssembly = layerAssembly;
this.name = name;
}
@Override
public LayerAssembly layer()
{
return layerAssembly;
}
@Override
public ModuleAssembly module( String layerName, String moduleName )
{
return layerAssembly.application().module( layerName, moduleName );
}
@Override
public ModuleAssembly setName( String name )
{
this.name = name;
return this;
}
@Override
public String name()
{
return name;
}
public ModuleAssembly setMetaInfo( Object info )
{
metaInfo.set( info );
return this;
}
@Override
@SafeVarargs
public final ModuleAssembly withActivators( Class<? extends Activator<Module>>... activators )
{
this.activators.addAll( Arrays.asList( activators ) );
return this;
}
@Override
@SuppressWarnings( {"raw", "unchecked"} )
public ValueDeclaration values( Class<?>... valueTypes )
{
List<ValueAssemblyImpl> assemblies = new ArrayList<>();
for( Class valueType : valueTypes )
{
if( valueAssemblies.containsKey( valueType ) )
{
assemblies.add( valueAssemblies.get( valueType ) );
}
else
{
ValueAssemblyImpl valueAssembly = new ValueAssemblyImpl( valueType );
valueAssemblies.put( valueType, valueAssembly );
assemblies.add( valueAssembly );
}
}
return new ValueDeclarationImpl( assemblies );
}
@Override
public ValueDeclaration values( Specification<? super ValueAssembly> specification )
{
List<ValueAssemblyImpl> assemblies = new ArrayList<>();
for( ValueAssemblyImpl transientAssembly : valueAssemblies.values() )
{
if( specification.satisfiedBy( transientAssembly ) )
{
assemblies.add( transientAssembly );
}
}
return new ValueDeclarationImpl( assemblies );
}
@Override
@SuppressWarnings( {"raw", "unchecked"} )
public TransientDeclaration transients( Class<?>... transientTypes )
{
List<TransientAssemblyImpl> assemblies = new ArrayList<>();
for( Class valueType : transientTypes )
{
if( transientAssemblies.containsKey( valueType ) )
{
assemblies.add( transientAssemblies.get( valueType ) );
}
else
{
TransientAssemblyImpl transientAssembly = new TransientAssemblyImpl( valueType );
transientAssemblies.put( valueType, transientAssembly );
assemblies.add( transientAssembly );
}
}
return new TransientDeclarationImpl( assemblies );
}
@Override
public TransientDeclaration transients( Specification<? super TransientAssembly> specification )
{
List<TransientAssemblyImpl> assemblies = new ArrayList<>();
for( TransientAssemblyImpl transientAssembly : transientAssemblies.values() )
{
if( specification.satisfiedBy( transientAssembly ) )
{
assemblies.add( transientAssembly );
}
}
return new TransientDeclarationImpl( assemblies );
}
@Override
@SuppressWarnings( {"raw", "unchecked"} )
public EntityDeclaration entities( Class<?>... entityTypes )
{
List<EntityAssemblyImpl> assemblies = new ArrayList<>();
for( Class entityType : entityTypes )
{
if( entityAssemblies.containsKey( entityType ) )
{
assemblies.add( entityAssemblies.get( entityType ) );
}
else
{
EntityAssemblyImpl entityAssembly = new EntityAssemblyImpl( entityType );
entityAssemblies.put( entityType, entityAssembly );
assemblies.add( entityAssembly );
}
}
return new EntityDeclarationImpl( assemblies );
}
@Override
public EntityDeclaration entities( Specification<? super EntityAssembly> specification )
{
List<EntityAssemblyImpl> assemblies = new ArrayList<>();
for( EntityAssemblyImpl entityAssembly : entityAssemblies.values() )
{
if( specification.satisfiedBy( entityAssembly ) )
{
assemblies.add( entityAssembly );
}
}
return new EntityDeclarationImpl( assemblies );
}
@Override
public ConfigurationDeclaration configurations( Class<?>... configurationTypes )
{
List<EntityAssemblyImpl> entityAssemblyList = new ArrayList<>();
for( Class entityType : configurationTypes )
{
if( this.entityAssemblies.containsKey( entityType ) )
{
entityAssemblyList.add( this.entityAssemblies.get( entityType ) );
}
else
{
EntityAssemblyImpl entityAssembly = new EntityAssemblyImpl( entityType );
this.entityAssemblies.put( entityType, entityAssembly );
entityAssemblyList.add( entityAssembly );
}
}
List<ValueAssemblyImpl> valueAssemblyList = new ArrayList<>();
for( Class valueType : configurationTypes )
{
if( valueAssemblies.containsKey( valueType ) )
{
valueAssemblyList.add( valueAssemblies.get( valueType ) );
}
else
{
ValueAssemblyImpl valueAssembly = new ValueAssemblyImpl( valueType );
valueAssemblies.put( valueType, valueAssembly );
valueAssemblyList.add( valueAssembly );
valueAssembly.types.add( Identity.class );
}
}
return new ConfigurationDeclarationImpl( entityAssemblyList, valueAssemblyList );
}
@Override
public ConfigurationDeclaration configurations( Specification<HasTypes> specification )
{
Specification<HasTypes> isConfigurationComposite = new MatchTypeSpecification( Identity.class );
specification = Specifications.and( specification, isConfigurationComposite );
List<EntityAssemblyImpl> entityAssmblyList = new ArrayList<>();
for( EntityAssemblyImpl entityAssembly : entityAssemblies.values() )
{
if( specification.satisfiedBy( entityAssembly ) )
{
entityAssmblyList.add( entityAssembly );
}
}
List<ValueAssemblyImpl> valueAssemblyList = new ArrayList<>();
for( ValueAssemblyImpl transientAssembly : valueAssemblies.values() )
{
if( specification.satisfiedBy( transientAssembly ) )
{
valueAssemblyList.add( transientAssembly );
}
}
return new ConfigurationDeclarationImpl( entityAssmblyList, valueAssemblyList );
}
@Override
public ObjectDeclaration objects( Class<?>... objectTypes )
throws AssemblyException
{
List<ObjectAssemblyImpl> assemblies = new ArrayList<>();
for( Class<?> objectType : objectTypes )
{
if( objectType.isInterface() )
{
throw new AssemblyException( "Interfaces can not be Zest Objects." );
}
if( objectAssemblies.containsKey( objectType ) )
{
assemblies.add( objectAssemblies.get( objectType ) );
}
else
{
ObjectAssemblyImpl objectAssembly = new ObjectAssemblyImpl( objectType );
objectAssemblies.put( objectType, objectAssembly );
assemblies.add( objectAssembly );
}
}
return new ObjectDeclarationImpl( assemblies );
}
@Override
public ObjectDeclaration objects( Specification<? super ObjectAssembly> specification )
{
List<ObjectAssemblyImpl> assemblies = new ArrayList<>();
for( ObjectAssemblyImpl objectAssembly : objectAssemblies.values() )
{
if( specification.satisfiedBy( objectAssembly ) )
{
assemblies.add( objectAssembly );
}
}
return new ObjectDeclarationImpl( assemblies );
}
@Override
public ServiceDeclaration addServices( Class<?>... serviceTypes )
{
List<ServiceAssemblyImpl> assemblies = new ArrayList<>();
for( Class<?> serviceType : serviceTypes )
{
ServiceAssemblyImpl serviceAssembly = new ServiceAssemblyImpl( serviceType );
serviceAssemblies.add( serviceAssembly );
assemblies.add( serviceAssembly );
}
return new ServiceDeclarationImpl( assemblies );
}
@Override
public ServiceDeclaration services( Class<?>... serviceTypes )
{
List<ServiceAssemblyImpl> assemblies = new ArrayList<>();
for( Class<?> serviceType : serviceTypes )
{
if( Iterables.matchesAny( AssemblySpecifications.types( serviceType ), serviceAssemblies ) )
{
Iterables.addAll( assemblies, Iterables.filter( AssemblySpecifications.types( serviceType ), serviceAssemblies ) );
}
else
{
ServiceAssemblyImpl serviceAssembly = new ServiceAssemblyImpl( serviceType );
serviceAssemblies.add( serviceAssembly );
assemblies.add( serviceAssembly );
}
}
return new ServiceDeclarationImpl( assemblies );
}
@Override
public ServiceDeclaration services( Specification<? super ServiceAssembly> specification )
{
List<ServiceAssemblyImpl> assemblies = new ArrayList<>();
for( ServiceAssemblyImpl serviceAssembly : serviceAssemblies )
{
if( specification.satisfiedBy( serviceAssembly ) )
{
assemblies.add( serviceAssembly );
}
}
return new ServiceDeclarationImpl( assemblies );
}
@Override
public ImportedServiceDeclaration importedServices( Class<?>... serviceTypes )
{
List<ImportedServiceAssemblyImpl> assemblies = new ArrayList<>();
for( Class<?> serviceType : serviceTypes )
{
if( importedServiceAssemblies.containsKey( serviceType ) )
{
assemblies.add( importedServiceAssemblies.get( serviceType ) );
}
else
{
ImportedServiceAssemblyImpl serviceAssembly = new ImportedServiceAssemblyImpl( serviceType, this );
importedServiceAssemblies.put( serviceType, serviceAssembly );
assemblies.add( serviceAssembly );
}
}
return new ImportedServiceDeclarationImpl( assemblies );
}
@Override
public ImportedServiceDeclaration importedServices( Specification<? super ImportedServiceAssembly> specification )
{
List<ImportedServiceAssemblyImpl> assemblies = new ArrayList<>();
for( ImportedServiceAssemblyImpl objectAssembly : importedServiceAssemblies.values() )
{
if( specification.satisfiedBy( objectAssembly ) )
{
assemblies.add( objectAssembly );
}
}
return new ImportedServiceDeclarationImpl( assemblies );
}
@Override
public <T> MixinDeclaration<T> forMixin( Class<T> mixinType )
{
return metaInfoDeclaration.on( mixinType );
}
@Override
public <ThrowableType extends Throwable> void visit( AssemblyVisitor<ThrowableType> visitor )
throws ThrowableType
{
visitor.visitModule( this );
for( TransientAssemblyImpl compositeDeclaration : transientAssemblies.values() )
{
visitor.visitComposite( new TransientDeclarationImpl( iterable( compositeDeclaration ) ) );
}
for( EntityAssemblyImpl entityDeclaration : entityAssemblies.values() )
{
visitor.visitEntity( new EntityDeclarationImpl( iterable( entityDeclaration ) ) );
}
for( ObjectAssemblyImpl objectDeclaration : objectAssemblies.values() )
{
visitor.visitObject( new ObjectDeclarationImpl( iterable( objectDeclaration ) ) );
}
for( ServiceAssemblyImpl serviceDeclaration : serviceAssemblies )
{
visitor.visitService( new ServiceDeclarationImpl( iterable( serviceDeclaration ) ) );
}
for( ImportedServiceAssemblyImpl importedServiceDeclaration : importedServiceAssemblies.values() )
{
visitor.visitImportedService( new ImportedServiceDeclarationImpl( iterable( importedServiceDeclaration ) ) );
}
for( ValueAssemblyImpl valueDeclaration : valueAssemblies.values() )
{
visitor.visitValue( new ValueDeclarationImpl( iterable( valueDeclaration ) ) );
}
}
ModuleModel assembleModule( AssemblyHelper helper )
throws AssemblyException
{
List<TransientModel> transientModels = new ArrayList<>();
List<ObjectModel> objectModels = new ArrayList<>();
List<ValueModel> valueModels = new ArrayList<>();
List<ServiceModel> serviceModels = new ArrayList<>();
List<ImportedServiceModel> importedServiceModels = new ArrayList<>();
if( name == null )
{
throw new AssemblyException( "Module must have name set" );
}
for( TransientAssemblyImpl compositeDeclaration : transientAssemblies.values() )
{
transientModels.add( compositeDeclaration.newTransientModel( metaInfoDeclaration, helper ) );
}
for( ValueAssemblyImpl valueDeclaration : valueAssemblies.values() )
{
valueModels.add( valueDeclaration.newValueModel( metaInfoDeclaration, helper ) );
}
List<EntityModel> entityModels = new ArrayList<>();
for( EntityAssemblyImpl entityDeclaration : entityAssemblies.values() )
{
entityModels.add( entityDeclaration.newEntityModel( metaInfoDeclaration,
metaInfoDeclaration,
metaInfoDeclaration,
metaInfoDeclaration,
helper ) );
}
for( ObjectAssemblyImpl objectDeclaration : objectAssemblies.values() )
{
objectDeclaration.addObjectModel( objectModels );
}
for( ServiceAssemblyImpl serviceDeclaration : serviceAssemblies )
{
if( serviceDeclaration.identity == null )
{
serviceDeclaration.identity = generateId( serviceDeclaration.types() );
}
serviceModels.add( serviceDeclaration.newServiceModel( metaInfoDeclaration, helper ) );
}
for( ImportedServiceAssemblyImpl importedServiceDeclaration : importedServiceAssemblies.values() )
{
importedServiceDeclaration.addImportedServiceModel( importedServiceModels );
}
ModuleModel moduleModel = new ModuleModel( name,
metaInfo,
new ActivatorsModel<>( activators ),
new TransientsModel( transientModels ),
new EntitiesModel( entityModels ),
new ObjectsModel( objectModels ),
new ValuesModel( valueModels ),
new ServicesModel( serviceModels ),
new ImportedServicesModel( importedServiceModels ) );
// Check for duplicate service identities
Set<String> identities = new HashSet<>();
for( ServiceModel serviceModel : serviceModels )
{
String identity = serviceModel.identity();
if( identities.contains( identity ) )
{
throw new DuplicateServiceIdentityException(
"Duplicated service identity: " + identity + " in module " + moduleModel.name()
);
}
identities.add( identity );
}
for( ImportedServiceModel serviceModel : importedServiceModels )
{
String identity = serviceModel.identity();
if( identities.contains( identity ) )
{
throw new DuplicateServiceIdentityException(
"Duplicated service identity: " + identity + " in module " + moduleModel.name()
);
}
identities.add( identity );
}
for( ImportedServiceModel importedServiceModel : importedServiceModels )
{
boolean found = false;
for( ObjectModel objectModel : objectModels )
{
if( first( objectModel.types() ).equals( importedServiceModel.serviceImporter() ) )
{
found = true;
break;
}
}
if( !found )
{
@SuppressWarnings( "raw" )
Class<? extends ServiceImporter> serviceFactoryType = importedServiceModel.serviceImporter();
ObjectModel objectModel = new ObjectModel( serviceFactoryType, Visibility.module, new MetaInfo() );
objectModels.add( objectModel );
}
}
return moduleModel;
}
private String generateId( Iterable<Class<?>> serviceTypes )
{
// Find service identity that is not yet used
Class<?> serviceType = serviceTypes.iterator()
.next(); // Use the first Iterable, which *SHOULD* be the main serviceType
int idx = 0;
String id = serviceType.getSimpleName();
boolean invalid;
do
{
invalid = false;
for( ServiceAssemblyImpl serviceAssembly : serviceAssemblies )
{
if( serviceAssembly.identity() != null && serviceAssembly.identity().equals( id ) )
{
idx++;
id = serviceType.getSimpleName() + "_" + idx;
invalid = true;
break;
}
}
}
while( invalid );
return id;
}
}