/*
* 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.isis.core.metamodel.services.container;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import com.google.common.base.Predicate;
import org.apache.isis.applib.DomainObjectContainer;
import org.apache.isis.applib.PersistFailedException;
import org.apache.isis.applib.RecoverableException;
import org.apache.isis.applib.RepositoryException;
import org.apache.isis.applib.annotation.DomainService;
import org.apache.isis.applib.annotation.NatureOfService;
import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.filter.Filter;
import org.apache.isis.applib.filter.Filters;
import org.apache.isis.applib.query.Query;
import org.apache.isis.applib.security.UserMemento;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizer;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerComposite;
import org.apache.isis.applib.services.exceprecog.ExceptionRecognizerForType;
import org.apache.isis.applib.services.factory.FactoryService;
import org.apache.isis.applib.services.i18n.TranslatableString;
import org.apache.isis.applib.services.message.MessageService;
import org.apache.isis.applib.services.registry.ServiceRegistry;
import org.apache.isis.applib.services.repository.RepositoryService;
import org.apache.isis.applib.services.title.TitleService;
import org.apache.isis.applib.services.user.UserService;
import org.apache.isis.applib.services.wrapper.WrapperFactory;
import org.apache.isis.applib.services.xactn.TransactionService;
import org.apache.isis.core.commons.ensure.Assert;
import org.apache.isis.core.commons.exceptions.IsisException;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
import org.apache.isis.core.metamodel.consent.InteractionResult;
import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet;
import org.apache.isis.core.metamodel.services.container.query.QueryFindByPattern;
import org.apache.isis.core.metamodel.services.container.query.QueryFindByTitle;
import org.apache.isis.core.metamodel.services.persistsession.PersistenceSessionServiceInternal;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
@DomainService(
nature = NatureOfService.DOMAIN,
menuOrder = "" + Integer.MAX_VALUE
)
public class DomainObjectContainerDefault
implements DomainObjectContainer, ExceptionRecognizer {
//region > titleOf
@Deprecated
@Programmatic
@Override
public String titleOf(final Object domainObject) {
return titleService.titleOf(domainObject);
}
//endregion
//region > iconNameOf
@Programmatic
@Override
public String iconNameOf(final Object domainObject) {
return titleService.iconNameOf(domainObject);
}
//endregion
//region > newXxxInstance, remove
@Deprecated
@Programmatic
@Override
@SuppressWarnings("unchecked")
public <T> T newTransientInstance(final Class<T> ofClass) {
return factoryService.instantiate(ofClass);
}
@Programmatic
@SuppressWarnings("unchecked")
@Override
public <T> T newViewModelInstance(Class<T> ofClass, String memento) {
final ObjectSpecification spec = specificationLoader.loadSpecification(ofClass);
if (!spec.containsFacet(ViewModelFacet.class)) {
throw new IsisException("Type must be a ViewModel: " + ofClass);
}
final ObjectAdapter adapter = persistenceSessionServiceInternal.createViewModelInstance(spec, memento);
if(adapter.getOid().isViewModel()) {
return (T)adapter.getObject();
} else {
throw new IsisException("Object instantiated but was not given a ViewModel Oid; please report as a possible defect in Isis: " + ofClass);
}
}
/**
* @deprecated - Aggregated objects are no longer supported
*/
@Deprecated
@Programmatic
@Override
@SuppressWarnings("unchecked")
public <T> T newAggregatedInstance(final Object parent, final Class<T> ofClass) {
throw new RuntimeException("Aggregated objects are no longer supported");
}
/**
* Returns a new instance of the specified class that will have been
* persisted.
*/
@Deprecated
@Programmatic
@Override
public <T> T newPersistentInstance(final Class<T> ofClass) {
final T newInstance = newTransientInstance(ofClass);
persist(newInstance);
return newInstance;
}
/**
* Returns a new instance of the specified class that has the same persisted
* state as the specified object.
*
* @deprecated - use {@link FactoryService#instantiate(Class)}.
*/
@Programmatic
@Override
@Deprecated
public <T> T newInstance(final Class<T> ofClass, final Object object) {
if (isPersistent(object)) {
return newPersistentInstance(ofClass);
} else {
return newTransientInstance(ofClass);
}
}
@Deprecated
@Programmatic
@Override
public <T> T mixin(final Class<T> mixinClass, final Object mixedIn) {
return factoryService.mixin(mixinClass, mixedIn);
}
@Programmatic
@Override
public void remove(final Object persistentObject) {
if (persistentObject == null) {
throw new IllegalArgumentException("Must specify a reference for disposing an object");
}
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(persistentObject));
if (!isPersistent(persistentObject)) {
throw new RepositoryException("Object not persistent: " + adapter);
}
persistenceSessionServiceInternal.remove(adapter);
}
@Programmatic
@Override
public void removeIfNotAlready(final Object object) {
repositoryService.remove(object);
}
//endregion
//region > injectServicesInto, lookupService, lookupServices (DEPRECATED)
/**
* @deprecated - use {@link ServiceRegistry#injectServicesInto(Object)} instead.
*/
@Deprecated
@Programmatic
@Override
public <T> T injectServicesInto(T domainObject) {
return serviceRegistry.injectServicesInto(domainObject);
}
/**
* @deprecated - use {@link ServiceRegistry#lookupService(Class)} instead.
*/
@Deprecated
@Programmatic
@Override
public <T> T lookupService(final Class<T> service) {
return serviceRegistry.lookupService(service);
}
/**
* @deprecated - use {@link ServiceRegistry#lookupServices(Class)} instead.
*/
@Deprecated
@Programmatic
@Override
public <T> Iterable<T> lookupServices(final Class<T> service) {
return serviceRegistry.lookupServices(service);
}
//endregion
//region > resolve, objectChanged (DEPRECATED)
/**
* Re-initialises the fields of an object, using the
* JDO {@link javax.jdo.PersistenceManager#refresh(Object) refresh} API.
*
* <p>
* Previously this method was provided for manual control of lazy loading; with the JDO/DataNucleus objectstore
* that original functionality is performed automatically by the framework.
* </p>
*
* @deprecated - equivalent to {@link org.apache.isis.applib.services.jdosupport.IsisJdoSupport#refresh(Object)}.
*/
@Programmatic
@Deprecated
@Override
public void resolve(final Object parent) {
persistenceSessionServiceInternal.resolve(unwrapped(parent));
}
/**
* Provided that the <tt>field</tt> parameter is <tt>null</tt>, re-initialises the fields of an object, using the
* JDO {@link javax.jdo.PersistenceManager#refresh(Object) refresh} API.
*
* <p>
* Previously this method was provided for manual control of lazy loading; with the JDO/DataNucleus objectstore
* that original functionality is performed automatically by the framework.
* </p>
*
* @deprecated - equivalent to {@link org.apache.isis.applib.services.jdosupport.IsisJdoSupport#refresh(Object)}.
*/
@Programmatic
@Deprecated
@Override
public void resolve(final Object parent, final Object field) {
persistenceSessionServiceInternal.resolve(unwrapped(parent), field);
}
/**
* @deprecated - no-op.
*/
@Deprecated
@Programmatic
@Override
public void objectChanged(final Object object) {
}
//endregion
//region > flush, commit (deprecated)
/**
* @deprecated
*/
@Deprecated
@Programmatic
@Override
public boolean flush() {
transactionService.flushTransaction();
return false;
}
/**
* @deprecated
*/
@Deprecated
@Programmatic
@Override
public void commit() {
persistenceSessionServiceInternal.commit();
}
//endregion
//region > isValid, validate
@Programmatic
@Override
public boolean isValid(final Object domainObject) {
return validate(domainObject) == null;
}
@Programmatic
@Override
public String validate(final Object domainObject) {
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(domainObject));
final InteractionResult validityResult =
adapter.getSpecification().isValidResult(adapter, InteractionInitiatedBy.FRAMEWORK);
return validityResult.getReason();
}
//endregion
//region > isViewModel
@Programmatic
@Override
public boolean isViewModel(final Object domainObject) {
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(domainObject));
return adapter.getSpecification().isViewModel();
}
//endregion
//region > persistence
@Programmatic
@Override
public boolean isPersistent(final Object domainObject) {
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(domainObject));
return adapter.representsPersistent();
}
/**
* {@inheritDoc}
*/
@Programmatic
@Override
public void persist(final Object domainObject) {
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(domainObject));
if(adapter == null) {
throw new PersistFailedException("Object not known to framework; instantiate using newTransientInstance(...) rather than simply new'ing up.");
}
if (adapter.isParentedCollection()) {
// TODO check aggregation is supported
return;
}
if (isPersistent(domainObject)) {
throw new PersistFailedException("Object already persistent; OID=" + adapter.getOid());
}
persistenceSessionServiceInternal.makePersistent(adapter);
}
/**
* {@inheritDoc}
*/
@Programmatic
@Override
public void persistIfNotAlready(final Object object) {
repositoryService.persist(object);
}
//endregion
//region > security (DEPRECATED)
@Deprecated
@Programmatic
@Override
public UserMemento getUser() {
return userService.getUser();
}
//endregion
//region > properties
@Deprecated
@Programmatic
@Override
public String getProperty(final String name) {
return configurationService.getProperty(name);
}
@Deprecated
@Programmatic
@Override
public String getProperty(final String name, final String defaultValue) {
return configurationService.getProperty(name, defaultValue);
}
@Deprecated
@Programmatic
@Override
public List<String> getPropertyNames() {
return configurationService.getPropertyNames();
}
//endregion
//region > info, warn, error messages
@Deprecated
@Programmatic
@Override
public void informUser(final String message) {
messageService.informUser(message);
}
@Deprecated
@Programmatic
@Override
public String informUser(final TranslatableString message, final Class<?> contextClass, final String contextMethod) {
return messageService.informUser(message, contextClass, contextMethod);
}
@Deprecated
@Programmatic
@Override
public void warnUser(final String message) {
messageService.warnUser(message);
}
@Deprecated
@Programmatic
@Override
public String warnUser(final TranslatableString message, final Class<?> contextClass, final String contextMethod) {
return messageService.warnUser(message, contextClass, contextMethod);
}
@Deprecated
@Programmatic
@Override
public void raiseError(final String message) {
messageService.raiseError(message);
}
@Deprecated
@Programmatic
@Override
public String raiseError(final TranslatableString message, final Class<?> contextClass, final String contextMethod) {
return messageService.raiseError(message, contextClass, contextMethod);
}
//endregion
//region > allInstances, allMatches, uniqueMatch, firstMatch
@Programmatic
@Override
public <T> List<T> allInstances(final Class<T> type, long... range) {
return repositoryService.allInstances(type, range);
}
// //////////////////////////////////////////////////////////////////
@Programmatic
@Override
public <T> List<T> allMatches(final Class<T> cls, final Predicate<? super T> predicate, long... range) {
return repositoryService.allMatches(cls, predicate, range);
}
@Programmatic
@Deprecated
@Override
public <T> List<T> allMatches(final Class<T> cls, final Filter<? super T> filter, long... range) {
return allMatches(cls, Filters.asPredicate(filter), range);
}
@Programmatic
@Override
public <T> List<T> allMatches(final Class<T> type, final T pattern, long... range) {
Assert.assertTrue("pattern not compatible with type", type.isAssignableFrom(pattern.getClass()));
return allMatches(new QueryFindByPattern<T>(type, pattern, range));
}
@Programmatic
@Override
public <T> List<T> allMatches(final Class<T> type, final String title, long... range) {
return allMatches(new QueryFindByTitle<T>(type, title, range));
}
@Programmatic
@Override
public <T> List<T> allMatches(final Query<T> query) {
return repositoryService.allMatches(query);
}
// //////////////////////////////////////////////////////////////////
@Programmatic
@Override
public <T> T firstMatch(final Class<T> cls, final Predicate<T> predicate) {
return repositoryService.firstMatch(cls, predicate);
}
@Programmatic
@Deprecated
@Override
public <T> T firstMatch(final Class<T> cls, final Filter<T> filter) {
return firstMatch(cls, Filters.asPredicate(filter));
}
@Programmatic
@Override
public <T> T firstMatch(final Class<T> type, final T pattern) {
final List<T> instances = allMatches(type, pattern, 0, 1); // No need to fetch more than 1
return firstInstanceElseNull(instances);
}
@Programmatic
@Override
public <T> T firstMatch(final Class<T> type, final String title) {
final List<T> instances = allMatches(type, title, 0, 1); // No need to fetch more than 1
return firstInstanceElseNull(instances);
}
@Programmatic
@Override
@SuppressWarnings("unchecked")
public <T> T firstMatch(final Query<T> query) {
// NB: this impl does NOT delegate to RepositoryService, because this implementation incorrectly always performs a flush
// irrespective of the autoflush setting. (The RepositoryService corrects that error).
flush(); // auto-flush any pending changes
final ObjectAdapter firstMatching = persistenceSessionServiceInternal.firstMatchingQuery(query);
return (T) ObjectAdapter.Util.unwrap(firstMatching);
}
// //////////////////////////////////////////////////////////////////
@Programmatic
@Override
public <T> T uniqueMatch(final Class<T> type, final Predicate<T> predicate) {
return repositoryService.uniqueMatch(type, predicate);
}
@Programmatic
@Deprecated
@Override
public <T> T uniqueMatch(final Class<T> type, final Filter<T> filter) {
final List<T> instances = allMatches(type, filter, 0, 2); // No need to fetch more than 2.
if (instances.size() > 1) {
throw new RepositoryException("Found more than one instance of " + type + " matching filter " + filter);
}
return firstInstanceElseNull(instances);
}
@Programmatic
@Override
public <T> T uniqueMatch(final Class<T> type, final T pattern) {
final List<T> instances = allMatches(type, pattern, 0, 2); // No need to fetch more than 2.
if (instances.size() > 1) {
throw new RepositoryException("Found more that one instance of " + type + " matching pattern " + pattern);
}
return firstInstanceElseNull(instances);
}
@Programmatic
@Override
public <T> T uniqueMatch(final Class<T> type, final String title) {
final List<T> instances = allMatches(type, title, 0, 2); // No need to fetch more than 2.
if (instances.size() > 1) {
throw new RepositoryException("Found more that one instance of " + type + " with title " + title);
}
return firstInstanceElseNull(instances);
}
@Programmatic
@Override
public <T> T uniqueMatch(final Query<T> query) {
return repositoryService.uniqueMatch(query);
}
private static <T> T firstInstanceElseNull(final List<T> instances) {
return instances.size() == 0 ? null : instances.get(0);
}
//endregion
//region > ExceptionRecognizer
static class ExceptionRecognizerForConcurrencyException
extends ExceptionRecognizerForType {
public ExceptionRecognizerForConcurrencyException() {
super(Category.CONCURRENCY, ConcurrencyException.class, prefix("Another user has just changed this data"));
}
}
static class ExceptionRecognizerForRecoverableException extends ExceptionRecognizerForType {
public ExceptionRecognizerForRecoverableException() {
super(Category.CLIENT_ERROR, RecoverableException.class);
}
}
private final ExceptionRecognizer recognizer =
new ExceptionRecognizerComposite(
new ExceptionRecognizerForConcurrencyException(),
new ExceptionRecognizerForRecoverableException()
);
/**
* Framework-provided implementation of {@link ExceptionRecognizer},
* which will automatically recognize any {@link org.apache.isis.applib.RecoverableException}s or
* any {@link ConcurrencyException}s.
*/
@Programmatic
@Override
public String recognize(Throwable ex) {
return recognizer.recognize(ex);
}
//endregion
//region > init, shutdown
@Programmatic
@PostConstruct
@Override
public void init(Map<String, String> properties) {
injectServicesInto(recognizer);
recognizer.init(properties);
}
@Programmatic
@PreDestroy
@Override
public void shutdown() {
recognizer.shutdown();
}
//endregion
//region > helpers
private Object unwrapped(Object domainObject) {
return wrapperFactory != null ? wrapperFactory.unwrap(domainObject) : domainObject;
}
//endregion
//region > service dependencies
@javax.inject.Inject
SpecificationLoader specificationLoader;
@javax.inject.Inject
org.apache.isis.applib.services.config.ConfigurationService configurationService;
@javax.inject.Inject
FactoryService factoryService;
@javax.inject.Inject
MessageService messageService;
@javax.inject.Inject
RepositoryService repositoryService;
@javax.inject.Inject
ServiceRegistry serviceRegistry;
@javax.inject.Inject
TransactionService transactionService;
@javax.inject.Inject
TitleService titleService;
@javax.inject.Inject
UserService userService;
@javax.inject.Inject
WrapperFactory wrapperFactory;
@javax.inject.Inject
PersistenceSessionServiceInternal persistenceSessionServiceInternal;
//endregion
}