All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nakedobjects.metamodel.specloader.NakedObjectReflectorAbstract Maven / Gradle / Ivy

The newest version!
package org.nakedobjects.metamodel.specloader;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.nakedobjects.metamodel.commons.ensure.Ensure.ensureThatArg;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.nakedobjects.metamodel.adapter.NakedObject;
import org.nakedobjects.metamodel.adapter.NakedObjectList;
import org.nakedobjects.metamodel.commons.debug.DebugInfo;
import org.nakedobjects.metamodel.commons.debug.DebugString;
import org.nakedobjects.metamodel.commons.ensure.Assert;
import org.nakedobjects.metamodel.commons.exceptions.NakedObjectException;
import org.nakedobjects.metamodel.commons.lang.JavaClassUtils;
import org.nakedobjects.metamodel.config.NakedObjectConfiguration;
import org.nakedobjects.metamodel.facetdecorator.FacetDecorator;
import org.nakedobjects.metamodel.facetdecorator.FacetDecoratorSet;
import org.nakedobjects.metamodel.facets.Facet;
import org.nakedobjects.metamodel.runtimecontext.RuntimeContext;
import org.nakedobjects.metamodel.runtimecontext.RuntimeContextAware;
import org.nakedobjects.metamodel.runtimecontext.noruntime.RuntimeContextNoRuntime;
import org.nakedobjects.metamodel.spec.IntrospectableSpecification;
import org.nakedobjects.metamodel.spec.JavaSpecification;
import org.nakedobjects.metamodel.spec.NakedObjectSpecification;
import org.nakedobjects.metamodel.spec.SpecificationFacets;
import org.nakedobjects.metamodel.specloader.classsubstitutor.ClassSubstitutor;
import org.nakedobjects.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
import org.nakedobjects.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistryDefault;
import org.nakedobjects.metamodel.specloader.internal.cache.SimpleSpecificationCache;
import org.nakedobjects.metamodel.specloader.internal.cache.SpecificationCache;
import org.nakedobjects.metamodel.specloader.internal.facetprocessor.FacetProcessor;
import org.nakedobjects.metamodel.specloader.internal.instances.InstanceCollectionSpecification;
import org.nakedobjects.metamodel.specloader.progmodelfacets.ProgrammingModelFacets;
import org.nakedobjects.metamodel.specloader.progmodelfacets.ProgrammingModelFacetsJava5;
import org.nakedobjects.metamodel.specloader.traverser.SpecificationTraverser;
import org.nakedobjects.metamodel.specloader.validator.MetaModelValidator;

/**
 * Builds the meta-model for Java 5 programming model.
 * 
 * 

* The implementation provides for a degree of pluggability: *

    *
  • The most important plug-in point is {@link ProgrammingModelFacets} that * specifies the set of {@link Facet} that make up programming model. If not * specified then defaults to {@link ProgrammingModelFacetsJava5} (which should * be used as a starting point for your own customizations). *
  • The only mandatory plug-in point is {@link ClassSubstitutor}, which * allows the class to be loaded to be substituted if required. This is used in * conjunction with some PersistenceMechanisms that do class * enhancement. *
  • The {@link CollectionTypeRegistry} specifies the types that should be * considered as collections. If not specified then will * {@link CollectionTypeRegistryDefault default}. (Note: this extension point * has not been tested, so should be considered more of a "statement of * intent" than actual API. Also, we may use annotations (similar to the * way in which Values are specified) as an alternative mechanism). *
* *

* In addition, the {@link RuntimeContext} can optionally be injected, but will * default to {@link RuntimeContextNoRuntime} if not provided prior to * {@link #init() initialization}. The purpose of {@link RuntimeContext} is to * allow the metamodel to be used standalone, for example in a Maven plugin. The * {@link RuntimeContextNoRuntime} implementation will through an exception for * any methods (such as finding an {@link NakedObject adapter}) because there is * no runtime session. In the case of the metamodel being used by the framework * (that is, when there is a runtime), then the framework injects an * implementation of {@link RuntimeContext} that acts like a bridge to its * NakedObjectsContext. */ public abstract class NakedObjectReflectorAbstract implements NakedObjectReflector, DebugInfo { private final static Logger LOG = Logger .getLogger(NakedObjectReflectorAbstract.class); /** * Injected in the constructor. */ private final NakedObjectConfiguration configuration; /** * Injected in the constructor. */ private final ClassSubstitutor classSubstitutor; /** * Injected in the constructor. */ private final CollectionTypeRegistry collectionTypeRegistry; /** * Injected in the constructor. */ private final ProgrammingModelFacets programmingModelFacets; /** * Defaulted in the constructor. */ private final FacetProcessor facetProcessor; /** * Defaulted in the constructor, so can be added to via * {@link #setFacetDecorators(FacetDecoratorSet)} or * {@link #addFacetDecorator(FacetDecorator)}. * *

* {@link FacetDecorator}s must be added prior to {@link #init() * initialization.} */ private final FacetDecoratorSet facetDecoratorSet; /** * Can optionally be injected, but will default (to * {@link RuntimeContextNoRuntime}) otherwise. * *

* Should be injected when used by framework, but will default to a no-op * implementation if the metamodel is being used standalone (eg for a * code-generator). */ private RuntimeContext runtimeContext; private SpecificationTraverser specificationTraverser; /** * Priming cache, optionally {@link #setServiceClasses(List) injected}. */ private List> serviceClasses = new ArrayList>(); /** * Optionally {@link #setValidator(MetaModelValidator) injected}. */ private MetaModelValidator metaModelValidator; /** * Defaulted in the constructor. */ private final SpecificationCache cache; // ///////////////////////////////////////////////////////////// // Constructor // ///////////////////////////////////////////////////////////// public NakedObjectReflectorAbstract( final NakedObjectConfiguration configuration, final ClassSubstitutor classSubstitutor, final CollectionTypeRegistry collectionTypeRegistry, final SpecificationTraverser specificationTraverser, final ProgrammingModelFacets programmingModelFacets, final Set facetDecorators, final MetaModelValidator metaModelValidator) { ensureThatArg(configuration, is(notNullValue())); ensureThatArg(classSubstitutor, is(notNullValue())); ensureThatArg(collectionTypeRegistry, is(notNullValue())); ensureThatArg(programmingModelFacets, is(notNullValue())); ensureThatArg(specificationTraverser, is(notNullValue())); ensureThatArg(facetDecorators, is(notNullValue())); ensureThatArg(metaModelValidator, is(notNullValue())); this.configuration = configuration; this.classSubstitutor = classSubstitutor; this.collectionTypeRegistry = collectionTypeRegistry; this.programmingModelFacets = programmingModelFacets; this.specificationTraverser = specificationTraverser; this.facetDecoratorSet = new FacetDecoratorSet(); for (final FacetDecorator facetDecorator : facetDecorators) { this.facetDecoratorSet.add(facetDecorator); } this.metaModelValidator = metaModelValidator; this.facetProcessor = new FacetProcessor(configuration, this, collectionTypeRegistry, programmingModelFacets); this.cache = new SimpleSpecificationCache(); } @Override protected void finalize() throws Throwable { super.finalize(); LOG.info("finalizing reflector factory " + this); } // ///////////////////////////////////////////////////////////// // init, shutdown // ///////////////////////////////////////////////////////////// /** * Initializes and wires up, and primes the cache based on * any service classes that may have been {@link #setServiceClasses(List) injected}. */ public void init() { if (LOG.isDebugEnabled()) { LOG.debug("initialising " + this); } // default subcomponents if (runtimeContext == null) { runtimeContext = new RuntimeContextNoRuntime(); } injectInto(runtimeContext); injectInto(specificationTraverser); injectInto(metaModelValidator); // wire subcomponents into each other runtimeContext.injectInto(facetProcessor); // initialize subcomponents facetDecoratorSet.init(); classSubstitutor.init(); collectionTypeRegistry.init(); specificationTraverser.init(); facetProcessor.init(); programmingModelFacets.init(); metaModelValidator.init(); // prime cache and validate primeCache(); metaModelValidator.validate(); } /** * load the service specifications and then, using the {@link #getSpecificationTraverser() traverser}, * keep loading all referenced specifications until we can find no more. */ private void primeCache() { for (Class serviceClass : serviceClasses) { internalLoadSpecification(serviceClass); } loadAllSpecifications(); } private void loadAllSpecifications() { List> newlyDiscoveredClasses = newlyDiscoveredClasses(); while(newlyDiscoveredClasses.size() > 0) { for(Class newClass: newlyDiscoveredClasses) { internalLoadSpecification(newClass); } newlyDiscoveredClasses = newlyDiscoveredClasses(); } } private List> newlyDiscoveredClasses() { List> newlyDiscoveredClasses = new ArrayList>(); NakedObjectSpecification[] noSpecs = allSpecifications(); try { for(NakedObjectSpecification noSpec: noSpecs) { getSpecificationTraverser().traverseReferencedClasses(noSpec, newlyDiscoveredClasses); } } catch (ClassNotFoundException ex) { throw new NakedObjectException(ex); } return newlyDiscoveredClasses; } public void shutdown() { LOG.info("shutting down " + this); getCache().clear(); facetDecoratorSet.shutdown(); } // ///////////////////////////////////////////////////////////// // install, load, allSpecifications // ///////////////////////////////////////////////////////////// /** * API: Return the specification for the specified class of object. */ public final NakedObjectSpecification loadSpecification( final String className) { ensureThatArg(className, is(notNullValue()), "specification class name must be specified"); try { final Class cls = loadBuiltIn(className); return internalLoadSpecification(cls); } catch (final ClassNotFoundException e) { final NakedObjectSpecification spec = getCache().get(className); if (spec == null) { throw new NakedObjectException( "No such class available: " + className); } return spec; } } /** * API: Return specification. */ public NakedObjectSpecification loadSpecification(final Class type) { return internalLoadSpecification(type); } private NakedObjectSpecification internalLoadSpecification(final Class type) { Class substitutedType = getClassSubstitutor().getClass(type); NakedObjectSpecification nakedObjectSpecification = substitutedType != null ? loadSpecificationForSubstitutedClass(substitutedType) : null; return nakedObjectSpecification; } private NakedObjectSpecification loadSpecificationForSubstitutedClass( final Class type) { Assert.assertNotNull(type); String typeName = type.getName(); SpecificationCache specificationCache = getCache(); synchronized (specificationCache) { final NakedObjectSpecification spec = specificationCache.get(typeName); if (spec != null) { return spec; } NakedObjectSpecification specification = createSpecification(type); if (specification == null) { throw new NakedObjectException( "Failed to create specification for class " + typeName); } // put into the cache prior to introspecting, to prevent // infinite loops specificationCache.cache(typeName, specification); introspectSpecificationIfRequired(specification); return specification; } } /** * Loads the specifications of the specified types except the one specified * (to prevent an infinite loop). */ public boolean loadSpecifications(List> typesToLoad, final Class typeToIgnore) { boolean anyLoadedAsNull = false; for (Class typeToLoad: typesToLoad) { if (typeToLoad != typeToIgnore) { NakedObjectSpecification noSpec = internalLoadSpecification(typeToLoad); boolean loadedAsNull = (noSpec == null); anyLoadedAsNull = loadedAsNull || anyLoadedAsNull; } } return anyLoadedAsNull; } public boolean loadSpecifications(List> typesToLoad) { return loadSpecifications(typesToLoad, null); } /** * Overridable method for language-specific subclass to create the * appropriate type of {@link NakedObjectSpecification}. */ protected NakedObjectSpecification createSpecification(final Class cls) { if (NakedObjectList.class.isAssignableFrom(cls)) { return new InstanceCollectionSpecification(this, getRuntimeContext()); } return new JavaSpecification(cls, this, getRuntimeContext()); } private Class loadBuiltIn(final String className) throws ClassNotFoundException { Class builtIn = JavaClassUtils.getBuiltIn(className); if (builtIn != null) { return builtIn; } return Class.forName(className); } /** * Return all the loaded specifications. */ public NakedObjectSpecification[] allSpecifications() { return getCache().allSpecifications(); } public boolean loaded(Class cls) { return loaded(cls.getName()); } public boolean loaded(String fullyQualifiedClassName) { return getCache().get(fullyQualifiedClassName) != null; } // added to try to track down a race condition // TODO: should probably remove private NakedObjectSpecification introspectSpecificationIfRequired( NakedObjectSpecification spec) { if (spec instanceof IntrospectableSpecification) { IntrospectableSpecification introspectableSpec = (IntrospectableSpecification) spec; if(!introspectableSpec.isIntrospected()) { introspectableSpec.introspect(this.facetDecoratorSet); } } return spec; } // //////////////////////////////////////////////////////////////////// // injectInto // //////////////////////////////////////////////////////////////////// /** * Injects self into candidate if required, and instructs its subcomponents * to do so also. */ public void injectInto(Object candidate) { Class candidateClass = candidate.getClass(); if (SpecificationLoaderAware.class.isAssignableFrom(candidateClass)) { SpecificationLoaderAware cast = SpecificationLoaderAware.class .cast(candidate); cast.setSpecificationLoader(this); } getClassSubstitutor().injectInto(candidate); getCollectionTypeRegistry().injectInto(candidate); } // ///////////////////////////////////////////////////////////// // Debugging // ///////////////////////////////////////////////////////////// public void debugData(final DebugString str) { facetDecoratorSet.debugData(str); str.appendln(); str.appendTitle("Specifications"); final NakedObjectSpecification[] specs = allSpecifications(); Arrays.sort(specs, new Comparator() { public int compare(final NakedObjectSpecification s1, final NakedObjectSpecification s2) { return s1.getShortName().compareToIgnoreCase(s2.getShortName()); } }); for (int i = 0; i < specs.length; i++) { final NakedObjectSpecification specification = specs[i]; str.append(specification.isAbstract() ? "A" : "."); str.append(specification.isService() ? "S" : "."); str.append(SpecificationFacets.isBoundedSet(specification) ? "B" : "."); str.append(specification.isCollection() ? "C" : "."); str.append(specification.isObject() ? "O" : "."); str.append("."); // placeholder for future support of maps str.append(specification.isParseable() ? "P" : "."); str.append(specification.isEncodeable() ? "E" : "."); str.append(specification.isValueOrIsAggregated() ? "A" : "."); str.append(!specification.isCollectionOrIsAggregated() ? "I" : "."); str.append(" "); str.append(specification.getShortName()); str.append(" [fqc="); str.append(specification.getFullName()); str.append(",type="); str.append(specification.getClass().getName()); str.appendln("]"); } } public String debugTitle() { return "Reflector"; } // ///////////////////////////////////////////////////////////// // Helpers (were previously injected, but no longer required) // ///////////////////////////////////////////////////////////// /** * Provides access to the registered {@link Facet}s. */ public FacetProcessor getFacetProcessor() { return facetProcessor; } private SpecificationCache getCache() { return cache; } // //////////////////////////////////////////////////////////////////// // Dependencies (injected by setter due to *Aware) // //////////////////////////////////////////////////////////////////// /** * As per {@link #setRuntimeContext(RuntimeContext)}. */ public RuntimeContext getRuntimeContext() { return runtimeContext; } /** * Due to {@link RuntimeContextAware}. */ public void setRuntimeContext(RuntimeContext runtimeContext) { this.runtimeContext = runtimeContext; } // //////////////////////////////////////////////////////////////////// // Dependencies (setters, optional) // //////////////////////////////////////////////////////////////////// public List> getServiceClasses() { return Collections.unmodifiableList(serviceClasses); } public void setServiceClasses(List> serviceClasses) { this.serviceClasses = serviceClasses; } // //////////////////////////////////////////////////////////////////// // Dependencies (injected from constructor) // //////////////////////////////////////////////////////////////////// public NakedObjectConfiguration getNakedObjectConfiguration() { return configuration; } public ClassSubstitutor getClassSubstitutor() { return classSubstitutor; } public CollectionTypeRegistry getCollectionTypeRegistry() { return collectionTypeRegistry; } public SpecificationTraverser getSpecificationTraverser() { return specificationTraverser; } public ProgrammingModelFacets getProgrammingModelFacets() { return programmingModelFacets; } public Set getFacetDecoratorSet() { return facetDecoratorSet.getFacetDecorators(); } public MetaModelValidator getMetaModelValidator() { return metaModelValidator; } } // Copyright (c) Naked Objects Group Ltd.





© 2015 - 2024 Weber Informatics LLC | Privacy Policy