http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationInstance.java new file mode 100644 index 0000000..e27a0af --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationInstance.java @@ -0,0 +1,174 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.stream.Stream; +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.common.MetaInfo; +import org.apache.polygene.api.structure.Application; +import org.apache.polygene.api.structure.ApplicationDescriptor; +import org.apache.polygene.api.structure.Layer; +import org.apache.polygene.api.structure.Module; +import org.apache.polygene.bootstrap.PolygeneRuntime; +import org.apache.polygene.runtime.activation.ActivationDelegate; + +/** + * Instance of a Polygene application. Contains a list of layers which are managed by this application + */ +public class ApplicationInstance + implements Application +{ + + // Constructor parameters + private final ApplicationModel applicationModel; + private final PolygeneRuntime runtime; + private final MetaInfo instanceMetaInfo; + // Eager instance objects + private final ActivationDelegate activation; + private final List<LayerInstance> layerInstances; + + public ApplicationInstance( ApplicationModel model, PolygeneRuntime runtime, MetaInfo instanceMetaInfo ) + { + // Constructor parameters + this.applicationModel = model; + this.runtime = runtime; + this.instanceMetaInfo = instanceMetaInfo; + + // Eager instance objects + activation = new ActivationDelegate( this ); + layerInstances = new ArrayList<>(); + } + + @Override + public String toString() + { + return name(); + } + + // Implementation of Application + @Override + public String name() + { + return applicationModel.name(); + } + + @Override + public String version() + { + return applicationModel.version(); + } + + @Override + public Mode mode() + { + return applicationModel.mode(); + } + + @Override + public Layer findLayer( String layerName ) + { + for( LayerInstance layerInstance : layerInstances ) + { + if( layerInstance.model().name().equals( layerName ) ) + { + return layerInstance; + } + } + + throw new IllegalArgumentException( "No such layer:" + layerName ); + } + + @Override + public Module findModule( String layerName, String moduleName ) + { + for( LayerInstance layerInstance : layerInstances ) + { + if( layerInstance.model().name().equals( layerName ) ) + { + return layerInstance.findModule( moduleName ); + } + } + + throw new IllegalArgumentException( "No such layer:" + layerName ); + } + + @Override + public Stream<? extends Layer> layers() + { + return layerInstances.stream(); + } + + @Override + public ApplicationDescriptor descriptor() + { + return applicationModel; + } + + // Implementation of MetaInfoHolder + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return instanceMetaInfo.get( infoType ); + } + + // Implementation of Activation + @Override + public void activate() + throws ActivationException + { + activation.activate( applicationModel.newActivatorsInstance(), layerInstances ); + } + + @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 + void addLayer( LayerInstance layer ) + { + layer.registerActivationEventListener( activation ); + layerInstances.add( layer ); + } + + public PolygeneRuntime runtime() + { + return runtime; + } + +}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationModel.java new file mode 100644 index 0000000..a163bed --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ApplicationModel.java @@ -0,0 +1,178 @@ +/* + * 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.polygene.api.PolygeneAPI; +import org.apache.polygene.api.activation.ActivationException; +import org.apache.polygene.api.common.InvalidApplicationException; +import org.apache.polygene.api.common.MetaInfo; +import org.apache.polygene.api.structure.Application; +import org.apache.polygene.api.structure.ApplicationDescriptor; +import org.apache.polygene.api.structure.LayerDescriptor; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.bootstrap.PolygeneRuntime; +import org.apache.polygene.runtime.activation.ActivatorsInstance; +import org.apache.polygene.runtime.activation.ActivatorsModel; +import org.apache.polygene.runtime.injection.InjectionProviderFactory; +import org.apache.polygene.runtime.injection.provider.InjectionProviderFactoryStrategy; + +/** + * JAVADOC + */ +public final class ApplicationModel + implements ApplicationDescriptor +{ + private final String name; + private final String version; + private final Application.Mode mode; + private final MetaInfo metaInfo; + private final ActivatorsModel<Application> activatorsModel; + private final List<LayerModel> layers; + private final InjectionProviderFactory ipf; + + public ApplicationModel( String name, + String version, + Application.Mode mode, + MetaInfo metaInfo, + ActivatorsModel<Application> activatorsModel, + List<LayerModel> layers + ) + { + this.name = name; + this.version = version; + this.mode = mode; + this.metaInfo = metaInfo; + this.activatorsModel = activatorsModel; + this.layers = layers; + ipf = new InjectionProviderFactoryStrategy( metaInfo ); + } + + @Override + public String name() + { + return name; + } + + public String version() + { + return version; + } + + public Application.Mode mode() + { + return mode; + } + + public MetaInfo metaInfo() + { + return metaInfo; + } + + public ActivatorsInstance<Application> newActivatorsInstance() + throws ActivationException + { + return new ActivatorsInstance<>( activatorsModel.newInstances() ); + } + + // SPI + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> visitor ) + throws ThrowableType + { + if( visitor.visitEnter( this ) ) + { + if( activatorsModel.accept( visitor ) ) + { + for( LayerModel layer : layers ) + { + if( !layer.accept( visitor ) ) + { + break; + } + } + } + } + return visitor.visitLeave( this ); + } + + @Override + public ApplicationInstance newInstance( PolygeneAPI runtime, Object... importedServiceInstances ) + throws InvalidApplicationException + { + MetaInfo instanceMetaInfo = new MetaInfo( metaInfo ); + for( Object importedServiceInstance : importedServiceInstances ) + { + instanceMetaInfo.set( importedServiceInstance ); + } + + ApplicationInstance applicationInstance = new ApplicationInstance( this, (PolygeneRuntime) runtime, instanceMetaInfo ); + + // Create layer instances + Map<LayerDescriptor, LayerDescriptor> layerInstanceMap = new HashMap<>(); + Map<LayerDescriptor, List<LayerDescriptor>> usedLayers = new HashMap<>(); + for( LayerModel layer : layers ) + { + List<LayerDescriptor> usedLayerInstances = new ArrayList<>(); + usedLayers.put( layer, usedLayerInstances ); + UsedLayersInstance usedLayersInstance = layer.usedLayers().newInstance( usedLayerInstances ); + LayerInstance layerInstance = layer.newInstance( applicationInstance ); + applicationInstance.addLayer( layerInstance ); + layerInstanceMap.put( layer, layerInstance.descriptor() ); + } + + // Resolve used layer instances + for( LayerModel layer : layers ) + { + List<LayerDescriptor> usedLayerInstances = usedLayers.get( layer ); + layer.usedLayers().layers().forEach( + usedLayer -> + { + LayerDescriptor layerDescriptor = layerInstanceMap.get( usedLayer ); + if( layerDescriptor == null ) + { + throw new InvalidApplicationException( "Could not find used layer:" + usedLayer + .name() ); + } + usedLayerInstances.add( layerDescriptor ); + } ); + } + + return applicationInstance; + } + + public InjectionProviderFactory injectionProviderFactory() + { + return ipf; + } + + @Override + public String toString() + { + return "ApplicationModel" + + "{name='" + name + '\'' + + ", version='" + version + '\'' + + ", mode=" + mode + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerInstance.java new file mode 100644 index 0000000..09201fb --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerInstance.java @@ -0,0 +1,150 @@ +/* + * 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.ArrayList; +import java.util.List; +import java.util.stream.Stream; +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.structure.Application; +import org.apache.polygene.api.structure.Layer; +import org.apache.polygene.api.structure.LayerDescriptor; +import org.apache.polygene.api.structure.Module; +import org.apache.polygene.runtime.activation.ActivationDelegate; + +/** + * Instance of a Polygene application layer. Contains a list of modules which are managed by this layer. + */ +public class LayerInstance + implements Layer +{ + + // Constructor parameters + private final LayerModel layerModel; + private final ApplicationInstance applicationInstance; + + // Eager instance objects + private final ActivationDelegate activation; + private final List<ModuleInstance> moduleInstances; + + public LayerInstance( LayerModel model, + ApplicationInstance applicationInstance + ) + { + // Constructor parameters + this.layerModel = model; + this.applicationInstance = applicationInstance; + + // Eager instance objects + activation = new ActivationDelegate( this ); + moduleInstances = new ArrayList<>(); + } + + @Override + public String toString() + { + return layerModel.toString(); + } + + // Implementation of Layer + @Override + public String name() + { + return layerModel.name(); + } + + @Override + public Application application() + { + return applicationInstance; + } + + // Implementation of MetaInfoHolder + @Override + public <T> T metaInfo( Class<T> infoType ) + { + return layerModel.metaInfo( infoType ); + } + + // Implementation of Activation + @Override + public void activate() + throws ActivationException + { + activation.activate( layerModel.newActivatorsInstance(), moduleInstances ); + } + + @Override + public void passivate() + throws PassivationException + { + activation.passivate(); + } + + @Override + public void registerActivationEventListener( ActivationEventListener listener ) + { + activation.registerActivationEventListener( listener ); + } + + @Override + public void deregisterActivationEventListener( ActivationEventListener listener ) + { + activation.deregisterActivationEventListener( listener ); + } + + @Override + public Stream<? extends Module> modules() + { + return moduleInstances.stream(); + } + + @Override + public LayerDescriptor descriptor() + { + return layerModel; + } + + void addModule( ModuleInstance module ) + { + module.registerActivationEventListener( activation ); + moduleInstances.add( module ); + } + + public LayerModel model() + { + return layerModel; + } + + /* package */ ModuleInstance findModule( String moduleName ) + { + for( ModuleInstance moduleInstance : moduleInstances ) + { + if( moduleInstance.model().name().equals( moduleName ) ) + { + return moduleInstance; + } + } + + throw new IllegalArgumentException( "No such module:" + moduleName ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerModel.java new file mode 100644 index 0000000..04ca846 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/LayerModel.java @@ -0,0 +1,170 @@ +/* + * 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.activation.ActivationException; +import org.apache.polygene.api.common.MetaInfo; +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.Layer; +import org.apache.polygene.api.structure.LayerDescriptor; +import org.apache.polygene.api.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.runtime.activation.ActivatorsInstance; +import org.apache.polygene.runtime.activation.ActivatorsModel; + +/** + * JAVADOC + */ +public final class LayerModel + implements LayerDescriptor, VisitableHierarchy<Object, Object> +{ + // Model + private final String name; + private final MetaInfo metaInfo; + private final UsedLayersModel usedLayersModel; + private final ActivatorsModel<Layer> activatorsModel; + private final List<ModuleModel> modules; + private LayerInstance layerInstance; + + public LayerModel( String name, + MetaInfo metaInfo, + UsedLayersModel usedLayersModel, + ActivatorsModel<Layer> activatorsModel, + List<ModuleModel> modules + ) + { + this.name = name; + this.metaInfo = metaInfo; + this.usedLayersModel = usedLayersModel; + this.activatorsModel = activatorsModel; + this.modules = modules; + } + + @Override + public String name() + { + return name; + } + + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + public Iterable<ModuleModel> modules() + { + return modules; + } + + @Override + public UsedLayersModel usedLayers() + { + return usedLayersModel; + } + + public ActivatorsInstance<Layer> newActivatorsInstance() + throws ActivationException + { + return new ActivatorsInstance<>( activatorsModel.newInstances() ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + if( activatorsModel.accept( modelVisitor ) ) + { + for( ModuleModel module : modules ) + { + if( !module.accept( modelVisitor ) ) + { + break; + } + } + } + } + return modelVisitor.visitLeave( this ); + } + + @Override + public Layer instance() + { + return layerInstance; + } + + public LayerInstance newInstance( ApplicationInstance applicationInstance ) + { + layerInstance = new LayerInstance( this, applicationInstance ); + for( ModuleModel module : modules ) + { + ModuleInstance moduleInstance = module.newInstance( this ); + layerInstance.addModule( moduleInstance ); + } + return layerInstance; + } + + @Override + public Stream<? extends ObjectDescriptor> visibleObjects( final Visibility visibility ) + { + return modules.stream().flatMap( module -> module.visibleObjects( visibility ) ); + } + + @Override + public Stream<? extends TransientDescriptor> visibleTransients( final Visibility visibility ) + { + return modules.stream().flatMap( module -> module.visibleTransients( visibility ) ); + } + + @Override + public Stream<? extends EntityDescriptor> visibleEntities( final Visibility visibility ) + { + return modules.stream().flatMap( module -> module.visibleEntities( visibility ) ); + } + + /* package */ + @Override + public Stream<? extends ValueDescriptor> visibleValues( final Visibility visibility ) + { + return modules.stream().flatMap( module -> module.visibleValues( visibility ) ); + } + + /* package */ + @Override + public Stream<? extends ModelDescriptor> visibleServices( final Visibility visibility ) + { + return modules.stream().flatMap( module -> module.visibleServices( visibility ) ); + } + + + @Override + public String toString() + { + return name; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleClassLoader.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleClassLoader.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleClassLoader.java new file mode 100644 index 0000000..65873f1 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleClassLoader.java @@ -0,0 +1,182 @@ +/* + * 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.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; +import java.util.stream.Stream; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.composite.AmbiguousTypeException; +import org.apache.polygene.api.composite.ModelDescriptor; + +import static java.util.stream.Stream.concat; +import static org.apache.polygene.api.common.Visibility.application; +import static org.apache.polygene.api.common.Visibility.module; +import static org.apache.polygene.api.util.Classes.modelTypeSpecification; + +// Module ClassLoader +class ModuleClassLoader + extends ClassLoader +{ + + private final ModuleModel moduleModel; + private final ConcurrentHashMap<String, Class<?>> classes = new ConcurrentHashMap<>(); + + ModuleClassLoader( ModuleModel moduleModel, ClassLoader classLoader ) + { + super( classLoader ); + this.moduleModel = moduleModel; + } + + @Override + protected Class<?> findClass( String className ) + throws ClassNotFoundException + { + try + { + Class<?> resultingClass = classes.computeIfAbsent( className, name -> + { + Predicate<ModelDescriptor> modelTypeSpecification = modelTypeSpecification( name ); + Stream<? extends ModelDescriptor> moduleModels = concat( + moduleModel.visibleObjects( Visibility.module ), + concat( + moduleModel.visibleEntities( Visibility.module ), + concat( + moduleModel.visibleTransients( Visibility.module ), + moduleModel.visibleValues( Visibility.module ) + ) + ) + ).filter( modelTypeSpecification ); + + Class<?> clazz = null; + Iterator<? extends ModelDescriptor> iterator = moduleModels.iterator(); + if( iterator.hasNext() ) + { + clazz = iterator.next().types().findFirst().orElse( null ); + + if( iterator.hasNext() ) + { + // Ambiguous exception + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + iterator.next() + ); + } + } + + // Check layer + if( clazz == null ) + { + Stream<? extends ModelDescriptor> modelsInLayer1 = concat( + moduleModel.layer().visibleObjects( Visibility.layer ), + concat( + moduleModel.layer().visibleEntities( Visibility.layer ), + concat( + moduleModel.layer().visibleTransients( Visibility.layer ), + moduleModel.layer().visibleValues( Visibility.layer ) + ) + ) + ); + // TODO: What does this actually represents?? Shouldn't 'application' visible models already be handed back from lasyerInstance().visibleXyz() ?? + Stream<? extends ModelDescriptor> modelsInLayer2 = concat( + moduleModel.layer().visibleObjects( Visibility.application ), + concat( + moduleModel.layer().visibleEntities( Visibility.application ), + concat( + moduleModel.layer().visibleTransients( Visibility.application ), + moduleModel.layer().visibleValues( Visibility.application ) + ) + ) + ); + Stream<? extends ModelDescriptor> layerModels = concat( + modelsInLayer1, + modelsInLayer2 + ).filter( modelTypeSpecification ); + + Iterator<? extends ModelDescriptor> layerModelsIter = layerModels.iterator(); + if( layerModelsIter.hasNext() ) + { + clazz = layerModelsIter.next().types().findFirst().orElse( null ); + + if( layerModelsIter.hasNext() ) + { + // Ambiguous exception + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + layerModelsIter.next() + ); + } + } + } + + // Check used layers + if( clazz == null ) + { + Stream<? extends ModelDescriptor> usedLayersModels = concat( + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleObjects( module ) ), + concat( + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleEntities( Visibility.layer ) ), + concat( + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleTransients( application ) ), + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleValues( application ) ) + ) + ) + ).filter( modelTypeSpecification ); + + Iterator<? extends ModelDescriptor> usedLayersModelsIter = usedLayersModels.iterator(); + if( usedLayersModelsIter.hasNext() ) + { + clazz = usedLayersModelsIter.next().types().findFirst().orElse( null ); + + if( usedLayersModelsIter.hasNext() ) + { + // Ambiguous exception + new AmbiguousTypeException( + "More than one model matches the classname " + name + ":" + usedLayersModelsIter.next() + ); + } + } + } + return clazz; + } ); + if( resultingClass == null ) + { + throw new ClassNotFoundException(); + } + return resultingClass; + } + catch( AmbiguousTypeException e ) + { + throw new ClassNotFoundException( className, e ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleInstance.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleInstance.java new file mode 100644 index 0000000..4eb9ac1 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleInstance.java @@ -0,0 +1,606 @@ +/* + * 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.NoSuchTransientException; +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.NoSuchObjectException; +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.service.NoSuchServiceException; +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.util.NullArgumentException; +import org.apache.polygene.api.value.NoSuchValueException; +import org.apache.polygene.api.value.ValueBuilder; +import org.apache.polygene.api.value.ValueBuilderFactory; +import org.apache.polygene.api.value.ValueComposite; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.api.value.ValueSerialization; +import org.apache.polygene.api.value.ValueSerializationException; +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.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.metrics.MetricsProviderAdapter; +import org.apache.polygene.spi.module.ModuleSpi; + +import static java.util.Arrays.asList; +import static java.util.stream.Stream.concat; + +/** + * 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 ValueSerialization valueSerialization; + private MetricsProvider metrics; + private UnitOfWorkFactory uowf; + + @SuppressWarnings( "LeakingThisInConstructor" ) + public 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 NoSuchObjectException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + ObjectDescriptor model = typeLookup.lookupObjectModel( mixinType ); + + if( model == null ) + { + throw new NoSuchObjectException( 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 + { + NullArgumentException.validateNotNull( "instance", instance ); + ObjectDescriptor model = typeLookup.lookupObjectModel( instance.getClass() ); + + if( model == null ) + { + throw new NoSuchObjectException( 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 NoSuchTransientException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + TransientDescriptor model = typeLookup.lookupTransientModel( mixinType ); + + if( model == null ) + { + throw new NoSuchTransientException( mixinType.getName(), name(), typeLookup ); + } + + Map<AccessibleObject, Property<?>> properties = new HashMap<>(); + model.state().properties().forEach( + propertyModel -> + { + Property<?> property = new PropertyInstance<>( ( (PropertyModel) propertyModel ).getBuilderInfo(), + propertyModel.initialValue( model.module() ) ); + 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 NoSuchTransientException, ConstructionException + { + return newTransientBuilder( mixinType ).use( uses ).newInstance(); + } + + // Implementation of ValueBuilderFactory + @Override + public <T> T newValue( Class<T> mixinType ) + throws NoSuchValueException, ConstructionException + { + return newValueBuilder( mixinType ).newInstance(); + } + + @Override + public <T> ValueBuilder<T> newValueBuilder( Class<T> mixinType ) + throws NoSuchValueException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + ValueDescriptor compositeModelModule = typeLookup.lookupValueModel( mixinType ); + + if( compositeModelModule == null ) + { + throw new NoSuchValueException( mixinType.getName(), name(), typeLookup ); + } + + 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 + ) + { + NullArgumentException.validateNotNull( "propertyFunction", propertyFunction ); + NullArgumentException.validateNotNull( "associationFunction", associationFunction ); + NullArgumentException.validateNotNull( "manyAssociationFunction", manyAssociationFunction ); + NullArgumentException.validateNotNull( "namedAssociationFunction", namedAssociationFunction ); + + ValueDescriptor compositeModelModule = typeLookup.lookupValueModel( mixinType ); + + if( compositeModelModule == null ) + { + throw new NoSuchValueException( mixinType.getName(), name(), typeLookup ); + } + + 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.initialValue( 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 ) + { + NullArgumentException.validateNotNull( "prototype", prototype ); + + ValueInstance valueInstance = ValueInstance.valueInstanceOf( (ValueComposite) prototype ); + Class<Composite> valueType = (Class<Composite>) valueInstance.types().findFirst().orElse( null ); + + ValueDescriptor model = typeLookup.lookupValueModel( valueType ); + + if( model == null ) + { + throw new NoSuchValueException( valueType.getName(), name(), typeLookup ); + } + + return new ValueBuilderWithPrototype<>( model, this, prototype ); + } + + @Override + public <T> T newValueFromSerializedState( Class<T> mixinType, String serializedState ) + throws NoSuchValueException, ConstructionException + { + NullArgumentException.validateNotNull( "mixinType", mixinType ); + ValueDescriptor model = typeLookup.lookupValueModel( mixinType ); + + if( model == null ) + { + throw new NoSuchValueException( mixinType.getName(), name(), typeLookup ); + } + + try + { + return valueSerialization().deserialize( model.module(), model.valueType(), serializedState ); + } + catch( ValueSerializationException 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 NoSuchServiceException + { + return findService( (Type) serviceType ); + } + + @Override + public <T> ServiceReference<T> findService( Type serviceType ) + { + ModelDescriptor serviceModel = typeLookup.lookupServiceModel( serviceType ); + if( serviceModel == null ) + { + throw new NoSuchServiceException( serviceType.getTypeName(), name(),typeLookup ); + } + return findServiceReferenceInstance( serviceModel ); + } + + @Override + public <T> Stream<ServiceReference<T>> findServices( final Class<T> serviceType ) + { + return findServices( (Type) serviceType ); + } + + @Override + 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 ); + } + + 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 + @SuppressWarnings( "unchecked" ) + 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; + } + + public LayerDescriptor layer() + { + return layer; + } + + @Override + public TypeLookup typeLookup() + { + return typeLookup; + } + + public EntityStore entityStore() + { + synchronized( this ) + { + if( store == null ) + { + try + { + ServiceReference<EntityStore> service = findService( EntityStore.class ); + store = service.get(); + } + catch( NoSuchServiceException e ) + { + throw new UnitOfWorkException( "No EntityStore service available in module " + name() ); + } + } + } + return store; + } + + public UnitOfWorkFactory unitOfWorkFactory() + { + synchronized( this ) + { + if( uowf == null ) + { + try + { + ServiceReference<UnitOfWorkFactory> service = findService( UnitOfWorkFactory.class ); + uowf = service.get(); + } + catch( NoSuchServiceException 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; + } + + public IdentityGenerator identityGenerator() + { + synchronized( this ) + { + if( generator == null ) + { + ServiceReference<IdentityGenerator> service = findService( IdentityGenerator.class ); + generator = service.get(); + } + return generator; + } + } + + public ValueSerialization valueSerialization() + { + synchronized( this ) + { + if( valueSerialization == null ) + { + try + { + ServiceReference<ValueSerialization> service = findService( ValueSerialization.class ); + valueSerialization = service.get(); + } + catch( NoSuchServiceException e ) + { + throw new ValueSerializationException( "No ValueSeriaservice available in module " + name() ); + } + } + } + return valueSerialization; + } + + public MetricsProvider metricsProvider() + { + synchronized( this ) + { + if( metrics == null ) + { + try + { + ServiceReference<MetricsProvider> service = findService( MetricsProvider.class ); + metrics = service.get(); + } + catch( NoSuchServiceException e ) + { + metrics = new MetricsProviderAdapter(); + } + } + } + return metrics; + } + +// public Stream<ServiceReference<?>> visibleServices( Visibility visibility ) +// { +// return concat( services.visibleServices( visibility ), +// importedServices.visibleServices( visibility ) ); +// } +// +// +// +// public Stream<ServiceReference<?>> findVisibleServiceTypes() +// { +// return concat( visibleServices( Visibility.module ), +// concat( +// layer().visibleServices( Visibility.layer ), +// concat( +// layer().visibleServices( Visibility.application ), +// layer().usedLayers().layers().flatMap( layer -> layer.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/ModuleModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleModel.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleModel.java new file mode 100644 index 0000000..f83a2cd --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/ModuleModel.java @@ -0,0 +1,394 @@ +/* + * 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.stream.Stream; +import org.apache.polygene.api.activation.ActivationException; +import org.apache.polygene.api.common.MetaInfo; +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.service.ImportedServiceDescriptor; +import org.apache.polygene.api.service.ServiceDescriptor; +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.util.HierarchicalVisitor; +import org.apache.polygene.api.util.VisitableHierarchy; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.runtime.activation.ActivatorsInstance; +import org.apache.polygene.runtime.activation.ActivatorsModel; +import org.apache.polygene.runtime.composite.TransientsModel; +import org.apache.polygene.runtime.entity.EntitiesModel; +import org.apache.polygene.runtime.object.ObjectsModel; +import org.apache.polygene.runtime.service.ImportedServicesModel; +import org.apache.polygene.runtime.service.ServicesModel; +import org.apache.polygene.runtime.value.ValuesModel; + +import static java.util.stream.Stream.concat; +import static org.apache.polygene.api.common.Visibility.application; +import static org.apache.polygene.api.common.Visibility.layer; +import static org.apache.polygene.api.common.Visibility.module; + +/** + * JAVADOC + */ +public class ModuleModel + implements ModuleDescriptor, VisitableHierarchy<Object, Object> +{ + private final LayerDescriptor layerModel; + private final ActivatorsModel<Module> activatorsModel; + private final TransientsModel transientsModel; + private final EntitiesModel entitiesModel; + private final ObjectsModel objectsModel; + private final ValuesModel valuesModel; + private final ServicesModel servicesModel; + private final ImportedServicesModel importedServicesModel; + private final TypeLookupImpl typeLookup; + private final ClassLoader classLoader; + + private final String name; + private final MetaInfo metaInfo; + private ModuleInstance moduleInstance; + + public ModuleModel( String name, + MetaInfo metaInfo, + LayerDescriptor layerModel, + ActivatorsModel<Module> activatorsModel, + TransientsModel transientsModel, + EntitiesModel entitiesModel, + ObjectsModel objectsModel, + ValuesModel valuesModel, + ServicesModel servicesModel, + ImportedServicesModel importedServicesModel + ) + { + this.name = name; + this.metaInfo = metaInfo; + this.layerModel = layerModel; + this.activatorsModel = activatorsModel; + this.transientsModel = transientsModel; + this.entitiesModel = entitiesModel; + this.objectsModel = objectsModel; + this.valuesModel = valuesModel; + this.servicesModel = servicesModel; + this.importedServicesModel = importedServicesModel; + typeLookup = new TypeLookupImpl( this ); + classLoader = new ModuleClassLoader( this, Thread.currentThread().getContextClassLoader() ); + } + + @Override + public String name() + { + return name; + } + + public <T> T metaInfo( Class<T> infoType ) + { + return metaInfo.get( infoType ); + } + + @Override + public LayerDescriptor layer() + { + return layerModel; + } + + @Override + public ClassLoader classLoader() + { + return classLoader; + } + + public ActivatorsInstance<Module> newActivatorsInstance() + throws ActivationException + { + return new ActivatorsInstance<>( activatorsModel.newInstances() ); + } + + @Override + public EntityDescriptor entityDescriptor( String name ) + { + try + { + Class<?> type = classLoader().loadClass( name ); + EntityDescriptor entityModel = typeLookup.lookupEntityModel( type ); + if( entityModel == null ) + { + return null; + } + return entityModel; + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + @Override + public ObjectDescriptor objectDescriptor( String typeName ) + { + try + { + Class<?> type = classLoader().loadClass( typeName ); + ObjectDescriptor objectModel = typeLookup.lookupObjectModel( type ); + if( objectModel == null ) + { + return null; + } + return objectModel; + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + @Override + public TransientDescriptor transientDescriptor( String name ) + { + try + { + Class<?> type = classLoader().loadClass( name ); + TransientDescriptor transientModel = typeLookup.lookupTransientModel( type ); + if( transientModel == null ) + { + return null; + } + return transientModel; + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + @Override + public ValueDescriptor valueDescriptor( String name ) + { + try + { + Class<?> type = classLoader().loadClass( name ); + ValueDescriptor valueModel = typeLookup.lookupValueModel( type ); + if( valueModel == null ) + { + return null; + } + return valueModel; + } + catch( ClassNotFoundException e ) + { + return null; + } + } + + @Override + public Module instance() + { + return moduleInstance; + } + + @Override + public TypeLookup typeLookup() + { + return typeLookup; + } + + public ModuleInstance newInstance( LayerDescriptor layerInstance ) + { + moduleInstance = new ModuleInstance( this, layerInstance, typeLookup, servicesModel, importedServicesModel ); + return moduleInstance; + } + + @Override + public Stream<? extends TransientDescriptor> transientComposites() + { + return transientsModel.stream(); + } + + @Override + public Stream<? extends ValueDescriptor> valueComposites() + { + return valuesModel.stream(); + } + + @Override + public Stream<? extends ServiceDescriptor> serviceComposites() + { + return servicesModel.models(); + } + + @Override + public Stream<? extends EntityDescriptor> entityComposites() + { + return entitiesModel.stream(); + } + + @Override + public Stream<? extends ImportedServiceDescriptor> importedServices() + { + return importedServicesModel.models(); + } + + @Override + public Stream<? extends ObjectDescriptor> objects() + { + return objectsModel.models(); + } + + public Stream<? extends ValueDescriptor> findVisibleValueTypes() + { + return concat( visibleValues( module ), + concat( + layer().visibleValues( layer ), + concat( + layer().visibleValues( application ), + layer().usedLayers().layers().flatMap( layer1 -> layer1.visibleValues( application ) ) + ) + ) + ); + } + + public Stream<? extends EntityDescriptor> findVisibleEntityTypes() + { + return concat( visibleEntities( module ), + concat( + layer().visibleEntities( layer ), + concat( + layer().visibleEntities( application ), + layer().usedLayers().layers().flatMap( layer1 -> layer1.visibleEntities( application ) ) + ) + ) + ); + } + + public Stream<? extends TransientDescriptor> findVisibleTransientTypes() + { + return concat( visibleTransients( module ), + concat( + layer().visibleTransients( layer ), + concat( + layer().visibleTransients( application ), + layer().usedLayers() + .layers() + .flatMap( layer1 -> layer1.visibleTransients( application ) ) + ) + ) + ); + } + + public Stream<? extends ModelDescriptor> findVisibleServiceTypes() + { + return concat( visibleServices( module ), + concat( + layer().visibleServices( layer ), + concat( + layer().visibleServices( application ), + layer().usedLayers().layers().flatMap( layer1 -> layer1.visibleServices( application ) ) + ) + ) + ); + } + + public Stream<? extends ObjectDescriptor> findVisibleObjectTypes() + { + return concat( visibleObjects( module ), + concat( + layer().visibleObjects( layer ), + concat( + layer().visibleObjects( application ), + layer().usedLayers().layers().flatMap( layer -> layer.visibleObjects( application ) ) + ) + ) + ); + } + + public Stream<? extends ObjectDescriptor> visibleObjects( Visibility visibility ) + { + return objectsModel.models() + .filter( new Visibilitypredicate( visibility ) ); + } + + public Stream<? extends TransientDescriptor> visibleTransients( Visibility visibility ) + { + return transientsModel.models() + .filter( new Visibilitypredicate( visibility ) ); + } + + public Stream<? extends EntityDescriptor> visibleEntities( Visibility visibility ) + { + return entitiesModel.models() + .filter( new Visibilitypredicate( visibility ) ); + } + + public Stream<? extends ValueDescriptor> visibleValues( Visibility visibility ) + { + return valuesModel.models() + .filter( new Visibilitypredicate( visibility ) ); + } + + public Stream<? extends ModelDescriptor> visibleServices( Visibility visibility ) + { + return concat( + servicesModel.models() + .filter( new Visibilitypredicate( visibility ) ), + importedServicesModel.models() + .filter( new Visibilitypredicate( visibility ) ) + ); + } + + @Override + public <ThrowableType extends Throwable> boolean accept( HierarchicalVisitor<? super Object, ? super Object, ThrowableType> modelVisitor ) + throws ThrowableType + { + if( modelVisitor.visitEnter( this ) ) + { + if( activatorsModel.accept( modelVisitor ) ) + { + if( transientsModel.accept( modelVisitor ) ) + { + if( entitiesModel.accept( modelVisitor ) ) + { + if( servicesModel.accept( modelVisitor ) ) + { + if( importedServicesModel.accept( modelVisitor ) ) + { + if( objectsModel.accept( modelVisitor ) ) + { + valuesModel.accept( modelVisitor ); + } + } + } + } + } + } + } + return modelVisitor.visitLeave( this ); + } + + @Override + public String toString() + { + return name; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/1c722f44/core/runtime/src/main/java/org/apache/polygene/runtime/structure/TypeLookupImpl.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/structure/TypeLookupImpl.java b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/TypeLookupImpl.java new file mode 100644 index 0000000..9df92c9 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/polygene/runtime/structure/TypeLookupImpl.java @@ -0,0 +1,543 @@ +/* + * 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.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.api.composite.AmbiguousTypeException; +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.ModuleDescriptor; +import org.apache.polygene.api.structure.TypeLookup; +import org.apache.polygene.api.type.HasTypes; +import org.apache.polygene.api.value.ValueDescriptor; + +import static java.util.stream.Collectors.toList; +import static java.util.stream.Stream.concat; +import static org.apache.polygene.api.common.Visibility.application; +import static org.apache.polygene.api.common.Visibility.layer; +import static org.apache.polygene.api.util.Classes.interfacesOf; + +/** + * Central place for Composite Type lookups. + */ +class TypeLookupImpl + implements TypeLookup +{ + private final LazyValue<List<ObjectDescriptor>> allObjects; + private final LazyValue<List<TransientDescriptor>> allTransients; + private final LazyValue<List<ValueDescriptor>> allValues; + private final LazyValue<List<EntityDescriptor>> allEntities; + private final LazyValue<List<? extends ModelDescriptor>> allServices; + private final ConcurrentHashMap<Class<?>, ObjectDescriptor> objectModels; + private final ConcurrentHashMap<Class<?>, TransientDescriptor> transientModels; + private final ConcurrentHashMap<Class<?>, ValueDescriptor> valueModels; + private final ConcurrentHashMap<Class<?>, List<EntityDescriptor>> entityModels; + private final ConcurrentHashMap<Class<?>, EntityDescriptor> unambiguousEntityModels; + private final ConcurrentHashMap<Type, ModelDescriptor> serviceModels; + private final ConcurrentHashMap<Type, List<? extends ModelDescriptor>> servicesReferences; + + private final ModuleDescriptor moduleModel; + + /** + * Create a new TypeLookup bound to the given ModuleModel. + * + * @param module ModuleModel bound to this TypeLookup + */ + TypeLookupImpl( ModuleModel module ) + { + moduleModel = module; + + // Instance caches + allObjects = new LazyValue<>(); + allTransients = new LazyValue<>(); + allValues = new LazyValue<>(); + allEntities = new LazyValue<>(); + allServices = new LazyValue<>(); + objectModels = new ConcurrentHashMap<>(); + transientModels = new ConcurrentHashMap<>(); + valueModels = new ConcurrentHashMap<>(); + entityModels = new ConcurrentHashMap<>(); + unambiguousEntityModels = new ConcurrentHashMap<>(); + serviceModels = new ConcurrentHashMap<>(); + servicesReferences = new ConcurrentHashMap<>(); + } + + @Override + public ObjectDescriptor lookupObjectModel( final Class<?> type ) + { + return objectModels.computeIfAbsent( type, key -> + { + List<? extends ObjectDescriptor> allModels = getAllObjects(); + ObjectDescriptor model = ambiguityMatching( key, allModels, new ExactTypeMatching<>( key ) ); + if( model == null ) + { + model = ambiguityMatching( key, allModels, new AssignableFromTypeMatching<>( key ) ); + } + return model; + } ); + } + + @Override + public TransientDescriptor lookupTransientModel( final Class<?> type ) + { + return transientModels.computeIfAbsent( type, key -> + { + List<? extends TransientDescriptor> allModels = getAllTransients(); + TransientDescriptor model = ambiguityMatching( key, allModels, new ExactTypeMatching<>( key ) ); + if( model == null ) + { + model = ambiguityMatching( key, allModels, new AssignableFromTypeMatching<>( key ) ); + } + return model; + } ); + } + + @Override + public ValueDescriptor lookupValueModel( final Class<?> type ) + { + return valueModels.computeIfAbsent( type, key -> + { + List<? extends ValueDescriptor> allModels = getAllValues(); + ValueDescriptor model = ambiguityMatching( key, allModels, new ExactTypeMatching<>( key ) ); + if( model == null ) + { + model = ambiguityMatching( key, allModels, new AssignableFromTypeMatching<>( key ) ); + } + return model; + } ); + } + + @Override + public EntityDescriptor lookupEntityModel( final Class<?> type ) + { + return unambiguousEntityModels.computeIfAbsent( type, key -> + { + List<? extends EntityDescriptor> allModels = getAllEntities(); + EntityDescriptor model = ambiguityMatching( key, allModels, new ExactTypeMatching<>( key ) ); + if( model == null ) + { + model = ambiguityMatching( key, allModels, new AssignableFromTypeMatching<>( key ) ); + } + return model; + } ); + } + + @Override + public List<EntityDescriptor> lookupEntityModels( final Class type ) + { + return entityModels.computeIfAbsent( + type, + key -> new TypeMatchesSelector<EntityDescriptor>( key ).selectFrom( allEntities() ) ); + } + + @Override + public ModelDescriptor lookupServiceModel( Type serviceType ) + { + return serviceModels.computeIfAbsent( + serviceType, + key -> new BestTypeMatchSelector<ModelDescriptor>( key ).selectFrom( allServices() ) + .bestMatchOrElse( null ) ); + } + + @Override + public List<? extends ModelDescriptor> lookupServiceModels( final Type type ) + { + return servicesReferences.computeIfAbsent( + type, + key -> new TypeMatchesSelector<ModelDescriptor>( key ).selectFrom( allServices() ) ); + } + + @Override + public Stream<ObjectDescriptor> allObjects() + { + return getAllObjects().stream(); + } + + private List<ObjectDescriptor> getAllObjects() + { + return allObjects.computeIfAbsent( + () -> concat( moduleModel.objects(), + concat( + concat( + moduleModel.layer().visibleObjects( layer ), + moduleModel.layer() + .visibleObjects( application ) + ), + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleObjects( application ) ) + ) + ).collect( toList() ) + ); + } + + @Override + public Stream<TransientDescriptor> allTransients() + { + return getAllTransients().stream(); + } + + private List<TransientDescriptor> getAllTransients() + { + return allTransients.computeIfAbsent( + () -> concat( moduleModel.transientComposites(), + concat( + concat( + moduleModel.layer().visibleTransients( layer ), + moduleModel.layer().visibleTransients( application ) + ), + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleTransients( application ) ) + ) + ).collect( toList() ) + ); + } + + @Override + public Stream<ValueDescriptor> allValues() + { + return getAllValues().stream(); + } + + private List<ValueDescriptor> getAllValues() + { + return allValues.computeIfAbsent( + () -> concat( moduleModel.valueComposites(), + concat( + concat( moduleModel.layer().visibleValues( layer ), + moduleModel.layer().visibleValues( application ) + ), + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer1 -> layer1.visibleValues( application ) ) + ) + ).collect( toList() ) + ); + } + + @Override + public Stream<EntityDescriptor> allEntities() + { + return getAllEntities().stream(); + } + + private List<EntityDescriptor> getAllEntities() + { + return allEntities.computeIfAbsent( + () -> concat( moduleModel.entityComposites(), + concat( + concat( + moduleModel.layer().visibleEntities( layer ), + moduleModel.layer().visibleEntities( application ) + ), + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleEntities( application ) ) + ) + ).collect( toList() ) + ); + } + + @Override + public Stream<? extends ModelDescriptor> allServices() + { + return getAllServices().stream(); + } + + private List<? extends ModelDescriptor> getAllServices() + { + return allServices.computeIfAbsent( + () -> concat( + concat( moduleModel.serviceComposites(), + concat( + concat( + moduleModel.layer().visibleServices( layer ), + moduleModel.layer().visibleServices( application ) + ), + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleServices( application ) ) + ) + ), + concat( moduleModel.importedServices(), + concat( + concat( + moduleModel.layer().visibleServices( layer ), + moduleModel.layer().visibleServices( application ) + ), + moduleModel.layer() + .usedLayers() + .layers() + .flatMap( layer -> layer.visibleServices( application ) ) + ) + ) + ).collect( toList() ) + ); + } + + private static <T extends ModelDescriptor> T ambiguityMatching( + Class type, + List<T> modelModules, + TypeMatching<T> matching + ) + { + List<T> models = modelModules.stream() + .filter( matching.and( new SameVisibility<>() ) ) + .distinct() + .collect( toList() ); + if( models.size() > 1 ) + { + throw new AmbiguousTypeException( "More than one type matches " + type.getName() + ": " + models + "]" ); + } + if( models.isEmpty() ) + { + return null; + } + return models.get( 0 ); + } + + private static abstract class TypeMatching<T extends HasTypes> + implements Predicate<T> + { + protected final Type lookedUpType; + + protected TypeMatching( Type lookedUpType ) + { + this.lookedUpType = lookedUpType; + } + + @Override + public final boolean test( T model ) + { + if( lookedUpType instanceof Class ) + { + return model.types().anyMatch( checkMatch( lookedUpType ) ); + } + else + { + if( lookedUpType instanceof ParameterizedType ) + { + // Foo<Bar> check + // First check Foo + ParameterizedType parameterizedType = (ParameterizedType) lookedUpType; + Type rawType = parameterizedType.getRawType(); + if( model.types().noneMatch( checkMatch( rawType ) ) ) + { + return false; + } + // Then check Bar + return interfacesOf( model.types() ).anyMatch( intf -> intf.equals( lookedUpType ) ); + } + else if( lookedUpType instanceof WildcardType ) + { + return true; + } + return false; + } + } + + protected abstract Predicate<Type> checkMatch( Type matchTo ); + } + + private static class ExactTypeMatching<T extends HasTypes> extends TypeMatching<T> + { + private ExactTypeMatching( Type lookedUpType ) + { + super( lookedUpType ); + } + + protected Predicate<Type> checkMatch( Type matchTo ) + { + return matchTo::equals; + } + } + + private static class AssignableFromTypeMatching<T extends HasTypes> extends TypeMatching<T> + { + private AssignableFromTypeMatching( Type lookedUpType ) + { + super( lookedUpType ); + } + + protected Predicate<Type> checkMatch( Type matchTo ) + { + // TODO; what to do if there is ParameterizedType here?? Now set to ClassCastException and see if anything surfaces +// if( matchTo instanceof Class ) + { + Class<?> clazz = (Class<?>) matchTo; + return candidate -> !candidate.equals( matchTo ) && clazz.isAssignableFrom( (Class<?>) candidate ); + } +// return candidate -> candidate.equals( matchTo ); + } + } + + /** + * Selects descriptors by combining {@link ExactTypeMatching} and {@link AssignableFromTypeMatching}. + * + * Selected descriptors are sorted, exact matches first, assignable ones second. + * Other than that, original order is preserved. + * + * <code> + * [ assignable1, matching1, assignable2, assignable3, matching2, non-matching-nor-assignable ] + * </code> + * results in + * <code> + * [ matching1, matching2, assignable1, assignable2, assignable3 ] + * </code> + * + * @param <T> Descriptor type + */ + private static class TypeMatchesSelector<T extends HasTypes> extends ArrayList<T> + { + private final ExactTypeMatching<T> exactMatchPredicate; + private final AssignableFromTypeMatching<T> assignablePredicate; + private Integer lastMatchIndex; + + private TypeMatchesSelector( Type type ) + { + this.exactMatchPredicate = new ExactTypeMatching<>( type ); + this.assignablePredicate = new AssignableFromTypeMatching<>( type ); + } + + List<T> selectFrom( Stream<? extends T> candidates ) + { + candidates.forEach( this::addDescriptor ); + return this; + } + + private void addDescriptor( T descriptor ) + { + if( contains( descriptor ) ) + { + return; + } + if( exactMatchPredicate.test( descriptor ) ) + { + Integer nextMatchIndex = lastMatchIndex == null ? 0 : lastMatchIndex + 1; + add( nextMatchIndex, descriptor ); + lastMatchIndex = nextMatchIndex; + } + else if( assignablePredicate.test( descriptor ) ) + { + add( descriptor ); + } + } + + boolean containsExactMatches() + { + return lastMatchIndex != null; + } + } + + /** + * Selects the best matching descriptor by combining {@link ExactTypeMatching} and {@link AssignableFromTypeMatching}. + * + * Selected descriptor is the first exact match if it exists, the first assignable otherwise. + * + * @param <T> Descriptor type + */ + private static class BestTypeMatchSelector<T extends HasTypes> + { + private TypeMatchesSelector<T> descriptors; + + BestTypeMatchSelector( Type type ) + { + this.descriptors = new TypeMatchesSelector<>( type ); + } + + BestTypeMatchSelector<T> selectFrom( Stream<? extends T> candidates ) + { + candidates.forEach( this::addDescriptor ); + return this; + } + + T bestMatchOrElse( T or ) + { + return !descriptors.isEmpty() ? descriptors.get( 0 ) : or; + } + + private void addDescriptor( T descriptor ) + { + // Until an exact match is found, even if we already found assignable ones, + // keep selecting in case the last element is an exact match. + if( !descriptors.containsExactMatches() ) + { + descriptors.addDescriptor( descriptor ); + } + } + } + + /** + * This Predicate will filter out all Models that doesn't have the same visibility as the first one. + */ + private static class SameVisibility<T extends ModelDescriptor> + implements Predicate<T> + { + private Visibility current = null; + + @Override + public boolean test( T model ) + { + if( current == null ) + { + current = model.visibility(); + return true; + } + return current == model.visibility(); + } + } + + private static class LazyValue<T> + { + private volatile T value; + + public T computeIfAbsent( Supplier<T> supplier ) + { + if( value == null ) + { + synchronized( this ) + { + if( value == null ) + { + value = supplier.get(); + } + } + } + return value; + } + } +}
