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.metamodel.services.container; 021 022import java.util.ArrayList; 023import java.util.List; 024import java.util.Map; 025import javax.annotation.PostConstruct; 026import javax.annotation.PreDestroy; 027import com.google.common.base.Predicate; 028import org.apache.isis.applib.*; 029import org.apache.isis.applib.filter.Filter; 030import org.apache.isis.applib.filter.Filters; 031import org.apache.isis.applib.query.Query; 032import org.apache.isis.applib.query.QueryFindAllInstances; 033import org.apache.isis.applib.security.RoleMemento; 034import org.apache.isis.applib.security.UserMemento; 035import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer; 036import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerComposite; 037import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerForType; 038import org.apache.isis.core.commons.authentication.AuthenticationSession; 039import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider; 040import org.apache.isis.core.commons.authentication.AuthenticationSessionProviderAware; 041import org.apache.isis.core.commons.ensure.Assert; 042import org.apache.isis.core.commons.exceptions.IsisException; 043import org.apache.isis.core.metamodel.adapter.*; 044import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 045import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware; 046import org.apache.isis.core.metamodel.adapter.oid.AggregatedOid; 047import org.apache.isis.core.metamodel.adapter.util.AdapterUtils; 048import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; 049import org.apache.isis.core.metamodel.consent.InteractionResult; 050import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; 051import org.apache.isis.core.metamodel.services.container.query.QueryFindByPattern; 052import org.apache.isis.core.metamodel.services.container.query.QueryFindByTitle; 053import org.apache.isis.core.metamodel.spec.ObjectSpecification; 054import org.apache.isis.core.metamodel.spec.SpecificationLoader; 055import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware; 056 057public class DomainObjectContainerDefault implements DomainObjectContainer, QuerySubmitterAware, ObjectDirtierAware, DomainObjectServicesAware, ObjectPersistorAware, SpecificationLoaderAware, AuthenticationSessionProviderAware, AdapterManagerAware, LocalizationProviderAware, ExceptionRecognizer { 058 059 private ObjectDirtier objectDirtier; 060 private ObjectPersistor objectPersistor; 061 private QuerySubmitter querySubmitter; 062 private SpecificationLoader specificationLookup; 063 private DomainObjectServices domainObjectServices; 064 private AuthenticationSessionProvider authenticationSessionProvider; 065 private AdapterManager adapterManager; 066 private LocalizationProvider localizationProvider; 067 068 public DomainObjectContainerDefault() { 069 } 070 071 // ////////////////////////////////////////////////////////////////// 072 // titleOf 073 // ////////////////////////////////////////////////////////////////// 074 075 @Override 076 public String titleOf(final Object domainObject) { 077 final ObjectAdapter objectAdapter = adapterManager.adapterFor(domainObject); 078 return objectAdapter.getSpecification().getTitle(objectAdapter, localizationProvider.getLocalization()); 079 } 080 081 // ////////////////////////////////////////////////////////////////// 082 // newInstance, disposeInstance 083 // ////////////////////////////////////////////////////////////////// 084 085 @Override 086 @SuppressWarnings("unchecked") 087 public <T> T newTransientInstance(final Class<T> ofClass) { 088 final ObjectSpecification spec = getSpecificationLookup().loadSpecification(ofClass); 089 if (spec.isParented()) { 090 return newAggregatedInstance(this, ofClass); 091 } else { 092 final ObjectAdapter adapter = doCreateTransientInstance(spec); 093 return (T) adapter.getObject(); 094 } 095 } 096 097 @SuppressWarnings("unchecked") 098 @Override 099 public <T extends ViewModel> T newViewModelInstance(Class<T> ofClass, String memento) { 100 final ObjectSpecification spec = getSpecificationLookup().loadSpecification(ofClass); 101 if (!spec.containsFacet(ViewModelFacet.class)) { 102 throw new IsisException("Type must be a ViewModel: " + ofClass); 103 } 104 final ObjectAdapter adapter = doCreateViewModelInstance(spec, memento); 105 if(adapter.getOid().isViewModel()) { 106 return (T)adapter.getObject(); 107 } else { 108 throw new IsisException("Object instantiated but was not given a ViewModel Oid; please report as a possible defect in Isis: " + ofClass); 109 } 110 } 111 112 @Override 113 @SuppressWarnings("unchecked") 114 public <T> T newAggregatedInstance(final Object parent, final Class<T> ofClass) { 115 final ObjectSpecification spec = getSpecificationLookup().loadSpecification(ofClass); 116 if (!spec.isParented()) { 117 throw new IsisException("Type must be annotated as @Aggregated: " + ofClass); 118 } 119 final ObjectAdapter adapter = doCreateAggregatedInstance(spec, parent); 120 if (adapter.getOid() instanceof AggregatedOid) { 121 return (T) adapter.getObject(); 122 } else { 123 throw new IsisException("Object instantiated but was not given a AggregatedOid (does the configured object store support aggregates?): " + ofClass); 124 } 125 } 126 127 /** 128 * Returns a new instance of the specified class that will have been 129 * persisted. 130 */ 131 @Override 132 public <T> T newPersistentInstance(final Class<T> ofClass) { 133 final T newInstance = newTransientInstance(ofClass); 134 persist(newInstance); 135 return newInstance; 136 } 137 138 /** 139 * Returns a new instance of the specified class that has the same persisted 140 * state as the specified object. 141 */ 142 @Override 143 public <T> T newInstance(final Class<T> ofClass, final Object object) { 144 if (isPersistent(object)) { 145 return newPersistentInstance(ofClass); 146 } else { 147 return newTransientInstance(ofClass); 148 } 149 } 150 151 /** 152 * Factored out as a potential hook method for subclasses. 153 */ 154 protected ObjectAdapter doCreateTransientInstance(final ObjectSpecification spec) { 155 return getDomainObjectServices().createTransientInstance(spec); 156 } 157 158 protected ObjectAdapter doCreateViewModelInstance(final ObjectSpecification spec, final String memento) { 159 return getDomainObjectServices().createViewModelInstance(spec, memento); 160 } 161 162 private ObjectAdapter doCreateAggregatedInstance(final ObjectSpecification spec, final Object parent) { 163 final ObjectAdapter parentAdapter = getAdapterManager().getAdapterFor(parent); 164 return getDomainObjectServices().createAggregatedInstance(spec, parentAdapter); 165 } 166 167 @Override 168 public void remove(final Object persistentObject) { 169 if (persistentObject == null) { 170 throw new IllegalArgumentException("Must specify a reference for disposing an object"); 171 } 172 final ObjectAdapter adapter = getAdapterManager().adapterFor(persistentObject); 173 if (!isPersistent(persistentObject)) { 174 throw new RepositoryException("Object not persistent: " + adapter); 175 } 176 177 getObjectPersistor().remove(adapter); 178 } 179 180 @Override 181 public void removeIfNotAlready(final Object object) { 182 if (!isPersistent(object)) { 183 return; 184 } 185 remove(object); 186 } 187 188 189 // ////////////////////////////////////////////////////////////////// 190 // injectServicesInto 191 // ////////////////////////////////////////////////////////////////// 192 193 @Override 194 public <T> T injectServicesInto(T domainObject) { 195 getDomainObjectServices().injectServicesInto(domainObject); 196 return domainObject; 197 } 198 199 // ////////////////////////////////////////////////////////////////// 200 // resolve, objectChanged 201 // ////////////////////////////////////////////////////////////////// 202 203 @Override 204 public void resolve(final Object parent) { 205 getDomainObjectServices().resolve(parent); 206 } 207 208 @Override 209 public void resolve(final Object parent, final Object field) { 210 getDomainObjectServices().resolve(parent, field); 211 } 212 213 @Override 214 public void objectChanged(final Object object) { 215 getObjectDirtier().objectChanged(object); 216 } 217 218 // ////////////////////////////////////////////////////////////////// 219 // flush, commit 220 // ////////////////////////////////////////////////////////////////// 221 222 @Override 223 public boolean flush() { 224 return getDomainObjectServices().flush(); 225 } 226 227 @Override 228 public void commit() { 229 getDomainObjectServices().commit(); 230 } 231 232 // ////////////////////////////////////////////////////////////////// 233 // isValid, validate 234 // ////////////////////////////////////////////////////////////////// 235 236 @Override 237 public boolean isValid(final Object domainObject) { 238 return validate(domainObject) == null; 239 } 240 241 @Override 242 public String validate(final Object domainObject) { 243 final ObjectAdapter adapter = getAdapterManager().adapterFor(domainObject); 244 final InteractionResult validityResult = adapter.getSpecification().isValidResult(adapter); 245 return validityResult.getReason(); 246 } 247 248 // ////////////////////////////////////////////////////////////////// 249 // persistence 250 // ////////////////////////////////////////////////////////////////// 251 252 @Override 253 public boolean isPersistent(final Object domainObject) { 254 final ObjectAdapter adapter = getAdapterManager().adapterFor(domainObject); 255 return adapter.representsPersistent(); 256 } 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override 262 public void persist(final Object domainObject) { 263 final ObjectAdapter adapter = getAdapterManager().adapterFor(domainObject); 264 265 if(adapter == null) { 266 throw new PersistFailedException("Object not known to framework; instantiate using newTransientInstance(...) rather than simply new'ing up."); 267 } 268 if (adapter.isParented()) { 269 // TODO check aggregation is supported 270 return; 271 } 272 if (isPersistent(domainObject)) { 273 throw new PersistFailedException("Object already persistent; OID=" + adapter.getOid()); 274 } 275 getObjectPersistor().makePersistent(adapter); 276 } 277 278 /** 279 * {@inheritDoc} 280 */ 281 @Override 282 public void persistIfNotAlready(final Object object) { 283 if (isPersistent(object)) { 284 return; 285 } 286 persist(object); 287 } 288 289 // ////////////////////////////////////////////////////////////////// 290 // security 291 // ////////////////////////////////////////////////////////////////// 292 293 @Override 294 public UserMemento getUser() { 295 final AuthenticationSession session = getAuthenticationSessionProvider().getAuthenticationSession(); 296 297 final String name = session.getUserName(); 298 final List<RoleMemento> roleMementos = asRoleMementos(session.getRoles()); 299 300 final UserMemento user = new UserMemento(name, roleMementos); 301 return user; 302 } 303 304 private List<RoleMemento> asRoleMementos(final List<String> roles) { 305 final List<RoleMemento> mementos = new ArrayList<RoleMemento>(); 306 if (roles != null) { 307 for (final String role : roles) { 308 mementos.add(new RoleMemento(role)); 309 } 310 } 311 return mementos; 312 } 313 314 // ////////////////////////////////////////////////////////////////// 315 // properties 316 // ////////////////////////////////////////////////////////////////// 317 318 @Override 319 public String getProperty(final String name) { 320 return getDomainObjectServices().getProperty(name); 321 } 322 323 @Override 324 public String getProperty(final String name, final String defaultValue) { 325 final String value = getProperty(name); 326 return value == null ? defaultValue : value; 327 } 328 329 @Override 330 public List<String> getPropertyNames() { 331 return getDomainObjectServices().getPropertyNames(); 332 } 333 334 // ////////////////////////////////////////////////////////////////// 335 // info, warn, error messages 336 // ////////////////////////////////////////////////////////////////// 337 338 @Override 339 public void informUser(final String message) { 340 getDomainObjectServices().informUser(message); 341 } 342 343 @Override 344 public void raiseError(final String message) { 345 getDomainObjectServices().raiseError(message); 346 } 347 348 @Override 349 public void warnUser(final String message) { 350 getDomainObjectServices().warnUser(message); 351 } 352 353 // ////////////////////////////////////////////////////////////////// 354 // allInstances 355 // ////////////////////////////////////////////////////////////////// 356 357 @Override 358 public <T> List<T> allInstances(final Class<T> type, long... range) { 359 return allMatches(new QueryFindAllInstances<T>(type, range)); 360 } 361 362 // ////////////////////////////////////////////////////////////////// 363 // allMatches 364 // ////////////////////////////////////////////////////////////////// 365 366 @Override 367 public <T> List<T> allMatches(final Class<T> cls, final Predicate<? super T> predicate, long... range) { 368 final List<T> allInstances = allInstances(cls, range); 369 final List<T> filtered = new ArrayList<T>(); 370 for (final T instance : allInstances) { 371 if (predicate.apply(instance)) { 372 filtered.add(instance); 373 } 374 } 375 return filtered; 376 } 377 378 @Deprecated 379 @Override 380 public <T> List<T> allMatches(final Class<T> cls, final Filter<? super T> filter, long... range) { 381 return allMatches(cls, Filters.asPredicate(filter), range); 382 } 383 384 @Override 385 public <T> List<T> allMatches(final Class<T> type, final T pattern, long... range) { 386 Assert.assertTrue("pattern not compatible with type", type.isAssignableFrom(pattern.getClass())); 387 return allMatches(new QueryFindByPattern<T>(type, pattern, range)); 388 } 389 390 @Override 391 public <T> List<T> allMatches(final Class<T> type, final String title, long... range) { 392 return allMatches(new QueryFindByTitle<T>(type, title, range)); 393 } 394 395 @Override 396 public <T> List<T> allMatches(final Query<T> query) { 397 flush(); // auto-flush any pending changes 398 final List<ObjectAdapter> allMatching = getQuerySubmitter().allMatchingQuery(query); 399 return AdapterUtils.unwrap(allMatching); 400 } 401 402 // ////////////////////////////////////////////////////////////////// 403 // firstMatch 404 // ////////////////////////////////////////////////////////////////// 405 406 @Override 407 public <T> T firstMatch(final Class<T> cls, final Predicate<T> predicate) { 408 final List<T> allInstances = allInstances(cls); // Have to fetch all, as matching is done in next loop 409 for (final T instance : allInstances) { 410 if (predicate.apply(instance)) { 411 return instance; 412 } 413 } 414 return null; 415 } 416 417 @Deprecated 418 @Override 419 public <T> T firstMatch(final Class<T> cls, final Filter<T> filter) { 420 return firstMatch(cls, Filters.asPredicate(filter)); 421 } 422 423 @Override 424 public <T> T firstMatch(final Class<T> type, final T pattern) { 425 final List<T> instances = allMatches(type, pattern, 0, 1); // No need to fetch more than 1 426 return firstInstanceElseNull(instances); 427 } 428 429 @Override 430 public <T> T firstMatch(final Class<T> type, final String title) { 431 final List<T> instances = allMatches(type, title, 0, 1); // No need to fetch more than 1 432 return firstInstanceElseNull(instances); 433 } 434 435 @Override 436 @SuppressWarnings("unchecked") 437 public <T> T firstMatch(final Query<T> query) { 438 flush(); // auto-flush any pending changes 439 final ObjectAdapter firstMatching = getQuerySubmitter().firstMatchingQuery(query); 440 return (T) AdapterUtils.unwrap(firstMatching); 441 } 442 443 // ////////////////////////////////////////////////////////////////// 444 // uniqueMatch 445 // ////////////////////////////////////////////////////////////////// 446 447 @Override 448 public <T> T uniqueMatch(final Class<T> type, final Predicate<T> predicate) { 449 final List<T> instances = allMatches(type, predicate, 0, 2); // No need to fetch more than 2. 450 if (instances.size() > 1) { 451 throw new RepositoryException("Found more than one instance of " + type + " matching filter " + predicate); 452 } 453 return firstInstanceElseNull(instances); 454 } 455 456 @Deprecated 457 @Override 458 public <T> T uniqueMatch(final Class<T> type, final Filter<T> filter) { 459 final List<T> instances = allMatches(type, filter, 0, 2); // No need to fetch more than 2. 460 if (instances.size() > 1) { 461 throw new RepositoryException("Found more than one instance of " + type + " matching filter " + filter); 462 } 463 return firstInstanceElseNull(instances); 464 } 465 466 @Override 467 public <T> T uniqueMatch(final Class<T> type, final T pattern) { 468 final List<T> instances = allMatches(type, pattern, 0, 2); // No need to fetch more than 2. 469 if (instances.size() > 1) { 470 throw new RepositoryException("Found more that one instance of " + type + " matching pattern " + pattern); 471 } 472 return firstInstanceElseNull(instances); 473 } 474 475 @Override 476 public <T> T uniqueMatch(final Class<T> type, final String title) { 477 final List<T> instances = allMatches(type, title, 0, 2); // No need to fetch more than 2. 478 if (instances.size() > 1) { 479 throw new RepositoryException("Found more that one instance of " + type + " with title " + title); 480 } 481 return firstInstanceElseNull(instances); 482 } 483 484 @Override 485 public <T> T uniqueMatch(final Query<T> query) { 486 final List<T> instances = allMatches(query); // No need to fetch more than 2. 487 if (instances.size() > 1) { 488 throw new RepositoryException("Found more that one instance for query:" + query.getDescription()); 489 } 490 return firstInstanceElseNull(instances); 491 } 492 493 private <T> T firstInstanceElseNull(final List<T> instances) { 494 return instances.size() == 0 ? null : instances.get(0); 495 } 496 497 498 /////////////////////////////////////////////////////////////// 499 // ExceptionRecognizer 500 /////////////////////////////////////////////////////////////// 501 502 static class ExceptionRecognizerForConcurrencyException extends ExceptionRecognizerForType { 503 public ExceptionRecognizerForConcurrencyException() { 504 super(ConcurrencyException.class, prefix("Another user has just changed this data")); 505 } 506 } 507 static class ExceptionRecognizerForRecoverableException extends ExceptionRecognizerForType { 508 public ExceptionRecognizerForRecoverableException() { 509 super(RecoverableException.class); 510 } 511 } 512 513 private final ExceptionRecognizer recognizer = 514 new ExceptionRecognizerComposite( 515 new ExceptionRecognizerForConcurrencyException(), 516 new ExceptionRecognizerForRecoverableException() 517 ); 518 519 /** 520 * Framework-provided implementation of {@link ExceptionRecognizer}, 521 * which will automatically recognize any {@link org.apache.isis.applib.RecoverableException}s or 522 * any {@link ConcurrencyException}s. 523 */ 524 @Override 525 public String recognize(Throwable ex) { 526 return recognizer.recognize(ex); 527 } 528 529 ExceptionRecognizer getRecognizer() { 530 return recognizer; 531 } 532 533 @PostConstruct 534 @Override 535 public void init(Map<String, String> properties) { 536 recognizer.init(properties); 537 } 538 539 @PreDestroy 540 @Override 541 public void shutdown() { 542 recognizer.shutdown(); 543 } 544 545 // ////////////////////////////////////////////////////////////////// 546 // Dependencies 547 // ////////////////////////////////////////////////////////////////// 548 549 protected QuerySubmitter getQuerySubmitter() { 550 return querySubmitter; 551 } 552 553 @Override 554 public void setQuerySubmitter(final QuerySubmitter querySubmitter) { 555 this.querySubmitter = querySubmitter; 556 } 557 558 protected DomainObjectServices getDomainObjectServices() { 559 return domainObjectServices; 560 } 561 562 @Override 563 public void setDomainObjectServices(final DomainObjectServices domainObjectServices) { 564 this.domainObjectServices = domainObjectServices; 565 } 566 567 protected SpecificationLoader getSpecificationLookup() { 568 return specificationLookup; 569 } 570 571 @Override 572 public void setSpecificationLookup(final SpecificationLoader specificationLookup) { 573 this.specificationLookup = specificationLookup; 574 } 575 576 protected AuthenticationSessionProvider getAuthenticationSessionProvider() { 577 return authenticationSessionProvider; 578 } 579 580 @Override 581 public void setAuthenticationSessionProvider(final AuthenticationSessionProvider authenticationSessionProvider) { 582 this.authenticationSessionProvider = authenticationSessionProvider; 583 } 584 585 protected AdapterManager getAdapterManager() { 586 return adapterManager; 587 } 588 589 @Override 590 public void setAdapterManager(final AdapterManager adapterManager) { 591 this.adapterManager = adapterManager; 592 } 593 594 protected ObjectDirtier getObjectDirtier() { 595 return objectDirtier; 596 } 597 598 @Override 599 public void setObjectDirtier(final ObjectDirtier objectDirtier) { 600 this.objectDirtier = objectDirtier; 601 } 602 603 protected ObjectPersistor getObjectPersistor() { 604 return objectPersistor; 605 } 606 607 @Override 608 public void setObjectPersistor(final ObjectPersistor objectPersistor) { 609 this.objectPersistor = objectPersistor; 610 } 611 612 @Override 613 public void setLocalizationProvider(final LocalizationProvider localizationProvider) { 614 this.localizationProvider = localizationProvider; 615 } 616 617 618 619 620}