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}