001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.progmodel.app;
021
022import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg;
023import static org.apache.isis.core.commons.ensure.Ensure.ensureThatState;
024import static org.hamcrest.CoreMatchers.is;
025import static org.hamcrest.CoreMatchers.notNullValue;
026
027import java.util.Arrays;
028import java.util.Collections;
029import java.util.List;
030import java.util.Set;
031import java.util.TreeSet;
032
033import com.google.common.collect.Lists;
034
035import org.apache.isis.applib.DomainObjectContainer;
036import org.apache.isis.core.commons.components.ApplicationScopedComponent;
037import org.apache.isis.core.commons.config.IsisConfiguration;
038import org.apache.isis.core.commons.config.IsisConfigurationDefault;
039import org.apache.isis.core.metamodel.facetdecorator.FacetDecorator;
040import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
041import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
042import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
043import org.apache.isis.core.metamodel.services.ServicesInjectorSpi;
044import org.apache.isis.core.metamodel.services.container.DomainObjectContainerDefault;
045import org.apache.isis.core.metamodel.spec.ObjectSpecification;
046import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi;
047import org.apache.isis.core.metamodel.specloader.ObjectReflectorDefault;
048import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor;
049import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutorAbstract;
050import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
051import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistryDefault;
052import org.apache.isis.core.metamodel.specloader.traverser.SpecificationTraverser;
053import org.apache.isis.core.metamodel.specloader.traverser.SpecificationTraverserDefault;
054import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
055import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
056import org.apache.isis.core.progmodel.metamodelvalidator.dflt.MetaModelValidatorDefault;
057
058/**
059 * Facade for the entire Isis metamodel and supporting components.
060 * 
061 * <p>
062 * Currently this is <i>not</i> used by Isis proper, but is available for use by integration tests.
063 * The intention is to factor it into <tt>IsisSystem</tt>.
064 */
065public class IsisMetaModel implements ApplicationScopedComponent {
066
067    private static enum State {
068        NOT_INITIALIZED, INITIALIZED, SHUTDOWN;
069    }
070
071    private final List<Object> services = Lists.newArrayList();
072
073    private State state = State.NOT_INITIALIZED;
074
075    private ObjectReflectorDefault reflector;
076    private RuntimeContext runtimeContext;
077
078    private IsisConfiguration configuration;
079    private ClassSubstitutor classSubstitutor;
080    private CollectionTypeRegistry collectionTypeRegistry;
081    private ProgrammingModel programmingModel;
082    private SpecificationTraverser specificationTraverser;
083    private Set<FacetDecorator> facetDecorators;
084    private MetaModelValidator metaModelValidator;
085
086    private DomainObjectContainer container;
087
088    private ValidationFailures validationFailures;
089
090    
091    public static class Builder {
092        private final RuntimeContext runtimeContext;
093        private final ProgrammingModel programmingModel;
094        private DomainObjectContainer container = new DomainObjectContainerDefault();
095        private List<Object> services = Lists.newArrayList();
096        
097        private Builder(RuntimeContext embeddedContext, ProgrammingModel programmingModel) {
098            this.runtimeContext = embeddedContext;
099            this.programmingModel = programmingModel;
100        }
101        
102        public Builder with(DomainObjectContainer container) {
103            this.container = container;
104            return this;
105        }
106        
107        public Builder withServices(Object... services) {
108            return withServices(Arrays.asList(services));
109        }
110        
111        public Builder withServices(List<Object> services) {
112            this.services = services;
113            return this;
114        }
115        
116        public IsisMetaModel build() {
117            final IsisMetaModel isisMetaModel = new IsisMetaModel(runtimeContext, programmingModel, services);
118            if(container != null) {
119                isisMetaModel.setContainer(container);
120            }
121            return isisMetaModel;
122        }
123    }
124    
125    public static Builder builder(RuntimeContext runtimeContext, ProgrammingModel programmingModel) {
126        return new Builder(runtimeContext, programmingModel);
127    }
128
129    public IsisMetaModel(final RuntimeContext runtimeContext, ProgrammingModel programmingModel, final List<Object> services) {
130        this(runtimeContext, programmingModel, services.toArray());
131    }
132    
133    public IsisMetaModel(final RuntimeContext runtimeContext, ProgrammingModel programmingModel, final Object... services) {
134        this.runtimeContext = runtimeContext;
135
136        setContainer(new DomainObjectContainerDefault());
137        this.services.addAll(Arrays.asList(services));
138        setConfiguration(new IsisConfigurationDefault());
139        setClassSubstitutor(new ClassSubstitutorAbstract() {});
140        setCollectionTypeRegistry(new CollectionTypeRegistryDefault());
141        setSpecificationTraverser(new SpecificationTraverserDefault());
142        setFacetDecorators(new TreeSet<FacetDecorator>());
143        setProgrammingModelFacets(programmingModel);
144
145        setMetaModelValidator(new MetaModelValidatorDefault());
146    }
147
148    private void setContainer(final DomainObjectContainer container) {
149        this.container = container;
150    }
151
152    /**
153     * The list of classes representing services, as specified in the
154     * {@link #IsisMetaModel(EmbeddedContext, Class...) constructor}.
155     * 
156     * <p>
157     * To obtain the instantiated services, use the
158     * {@link ServicesInjectorSpi#getRegisteredServices()} (available from
159     * {@link #getServicesInjector()}).
160     */
161    public List<Object> getServices() {
162        return Collections.unmodifiableList(services);
163    }
164
165    // ///////////////////////////////////////////////////////
166    // init, shutdown
167    // ///////////////////////////////////////////////////////
168
169    @Override
170    public void init() {
171        ensureNotInitialized();
172        reflector = new ObjectReflectorDefault(configuration, classSubstitutor, collectionTypeRegistry, specificationTraverser, programmingModel, facetDecorators, metaModelValidator);
173
174        reflector.setContainer(container);
175        reflector.setServices(services);
176        
177        runtimeContext.injectInto(container);
178        runtimeContext.setContainer(container);
179        runtimeContext.injectInto(reflector);
180        reflector.injectInto(runtimeContext);
181
182        validationFailures = reflector.initAndValidate();
183        runtimeContext.init();
184
185        for (final Object service : services) {
186            final ObjectSpecification serviceSpec = reflector.loadSpecification(service.getClass());
187            serviceSpec.markAsService();
188        }
189
190        state = State.INITIALIZED;
191    }
192    
193    public ValidationFailures getValidationFailures() {
194        return validationFailures;
195    }
196
197    @Override
198    public void shutdown() {
199        ensureInitialized();
200        state = State.SHUTDOWN;
201    }
202
203    // ///////////////////////////////////////////////////////
204    // SpecificationLoader
205    // ///////////////////////////////////////////////////////
206
207    /**
208     * Available once {@link #init() initialized}.
209     */
210    public SpecificationLoaderSpi getSpecificationLoader() {
211        return reflector;
212    }
213
214    // ///////////////////////////////////////////////////////
215    // DomainObjectContainer
216    // ///////////////////////////////////////////////////////
217
218    /**
219     * Available once {@link #init() initialized}.
220     */
221    public DomainObjectContainer getDomainObjectContainer() {
222        ensureInitialized();
223        return container;
224    }
225
226    // ///////////////////////////////////////////////////////
227    // DependencyInjector
228    // ///////////////////////////////////////////////////////
229
230    /**
231     * Available once {@link #init() initialized}.
232     */
233    public ServicesInjector getDependencyInjector() {
234        ensureInitialized();
235        return runtimeContext.getDependencyInjector();
236    }
237
238    // ///////////////////////////////////////////////////////
239    // Override defaults
240    // ///////////////////////////////////////////////////////
241
242    /**
243     * The {@link IsisConfiguration} in force, either defaulted or specified
244     * {@link #setConfiguration(IsisConfiguration) explicitly.}
245     */
246    public IsisConfiguration getConfiguration() {
247        return configuration;
248    }
249
250    /**
251     * Optionally specify the {@link IsisConfiguration}.
252     * 
253     * <p>
254     * Call prior to {@link #init()}.
255     */
256    public void setConfiguration(final IsisConfiguration configuration) {
257        ensureNotInitialized();
258        ensureThatArg(configuration, is(notNullValue()));
259        this.configuration = configuration;
260    }
261
262    /**
263     * The {@link ClassSubstitutor} in force, either defaulted or specified
264     * {@link #setClassSubstitutor(ClassSubstitutor) explicitly}.
265     */
266    public ClassSubstitutor getClassSubstitutor() {
267        return classSubstitutor;
268    }
269
270    /**
271     * Optionally specify the {@link ClassSubstitutor}.
272     * 
273     * <p>
274     * Call prior to {@link #init()}.
275     */
276    public void setClassSubstitutor(final ClassSubstitutor classSubstitutor) {
277        ensureNotInitialized();
278        ensureThatArg(classSubstitutor, is(notNullValue()));
279        this.classSubstitutor = classSubstitutor;
280    }
281
282    /**
283     * The {@link CollectionTypeRegistry} in force, either defaulted or
284     * specified {@link #setCollectionTypeRegistry(CollectionTypeRegistry)
285     * explicitly.}
286     */
287    public CollectionTypeRegistry getCollectionTypeRegistry() {
288        return collectionTypeRegistry;
289    }
290
291    /**
292     * Optionally specify the {@link CollectionTypeRegistry}.
293     * 
294     * <p>
295     * Call prior to {@link #init()}.
296     */
297    public void setCollectionTypeRegistry(final CollectionTypeRegistry collectionTypeRegistry) {
298        ensureNotInitialized();
299        ensureThatArg(collectionTypeRegistry, is(notNullValue()));
300        this.collectionTypeRegistry = collectionTypeRegistry;
301    }
302
303    /**
304     * The {@link SpecificationTraverser} in force, either defaulted or
305     * specified {@link #setSpecificationTraverser(SpecificationTraverser)
306     * explicitly}.
307     */
308    public SpecificationTraverser getSpecificationTraverser() {
309        return specificationTraverser;
310    }
311
312    /**
313     * Optionally specify the {@link SpecificationTraverser}.
314     */
315    public void setSpecificationTraverser(final SpecificationTraverser specificationTraverser) {
316        this.specificationTraverser = specificationTraverser;
317    }
318
319    /**
320     * The {@link ProgrammingModel} in force, either defaulted or specified
321     * {@link #setProgrammingModelFacets(ProgrammingModel) explicitly}.
322     */
323    public ProgrammingModel getProgrammingModelFacets() {
324        return programmingModel;
325    }
326
327    /**
328     * Optionally specify the {@link ProgrammingModel}.
329     * 
330     * <p>
331     * Call prior to {@link #init()}.
332     */
333    public void setProgrammingModelFacets(final ProgrammingModel programmingModel) {
334        ensureNotInitialized();
335        ensureThatArg(programmingModel, is(notNullValue()));
336        this.programmingModel = programmingModel;
337    }
338
339    /**
340     * The {@link FacetDecorator}s in force, either defaulted or specified
341     * {@link #setFacetDecorators(Set) explicitly}.
342     */
343    public Set<FacetDecorator> getFacetDecorators() {
344        return Collections.unmodifiableSet(facetDecorators);
345    }
346
347    /**
348     * Optionally specify the {@link FacetDecorator}s.
349     * 
350     * <p>
351     * Call prior to {@link #init()}.
352     */
353    public void setFacetDecorators(final Set<FacetDecorator> facetDecorators) {
354        ensureNotInitialized();
355        ensureThatArg(facetDecorators, is(notNullValue()));
356        this.facetDecorators = facetDecorators;
357    }
358
359    /**
360     * The {@link MetaModelValidator} in force, either defaulted or specified
361     * {@link #setMetaModelValidator(MetaModelValidator) explicitly}.
362     */
363    public MetaModelValidator getMetaModelValidator() {
364        return metaModelValidator;
365    }
366
367    /**
368     * Optionally specify the {@link MetaModelValidator}.
369     */
370    public void setMetaModelValidator(final MetaModelValidator metaModelValidator) {
371        this.metaModelValidator = metaModelValidator;
372    }
373
374    // ///////////////////////////////////////////////////////
375    // State management
376    // ///////////////////////////////////////////////////////
377
378    private State ensureNotInitialized() {
379        return ensureThatState(state, is(State.NOT_INITIALIZED));
380    }
381
382    private State ensureInitialized() {
383        return ensureThatState(state, is(State.INITIALIZED));
384    }
385
386}