/*
* 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.repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import com.google.common.base.Predicate;
import org.apache.isis.applib.PersistFailedException;
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.query.Query;
import org.apache.isis.applib.query.QueryFindAllInstances;
import org.apache.isis.applib.services.factory.FactoryService;
import org.apache.isis.applib.services.repository.RepositoryService;
import org.apache.isis.applib.services.wrapper.WrapperFactory;
import org.apache.isis.applib.services.xactn.TransactionService;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.services.persistsession.PersistenceSessionServiceInternal;
@DomainService(
nature = NatureOfService.DOMAIN,
menuOrder = "" + Integer.MAX_VALUE
)
public class RepositoryServiceInternalDefault implements RepositoryService {
private boolean autoFlush;
@Programmatic
@PostConstruct
public void init(Map<String, String> properties) {
final boolean disableAutoFlush = Boolean.parseBoolean(properties.get(KEY_DISABLE_AUTOFLUSH));
this.autoFlush = !disableAutoFlush;
}
// //////////////////////////////////////
@Programmatic
@Override
public <T> T instantiate(final Class<T> domainClass) {
return factoryService.instantiate(domainClass);
}
// //////////////////////////////////////
@Programmatic
@Override
public boolean isPersistent(final Object domainObject) {
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(domainObject));
return adapter.representsPersistent();
}
@Programmatic
@Override
public void persist(final Object object) {
if (isPersistent(object)) {
return;
}
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(object));
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(object)) {
throw new PersistFailedException("Object already persistent; OID=" + adapter.getOid());
}
persistenceSessionServiceInternal.makePersistent(adapter);
}
@Programmatic
@Override
public void persistAndFlush(final Object object) {
persist(object);
transactionService.flushTransaction();
}
@Override
@Programmatic
public void remove(final Object domainObject) {
removeIfNotAlready(domainObject);
}
private void removeIfNotAlready(final Object object) {
if (!isPersistent(object)) {
return;
}
if (object == null) {
throw new IllegalArgumentException("Must specify a reference for disposing an object");
}
final ObjectAdapter adapter = persistenceSessionServiceInternal.adapterFor(unwrapped(object));
if (!isPersistent(object)) {
throw new RepositoryException("Object not persistent: " + adapter);
}
persistenceSessionServiceInternal.remove(adapter);
}
@Override
@Programmatic
public void removeAndFlush(final Object domainObject) {
remove(domainObject);
transactionService.flushTransaction();
}
// //////////////////////////////////////
//region > allInstances, allMatches, uniqueMatch, firstMatch
@Programmatic
@Override
public <T> List<T> allInstances(final Class<T> type, long... range) {
return allMatches(new QueryFindAllInstances<T>(type, range));
}
// //////////////////////////////////////
@Programmatic
@Override
public <T> List<T> allMatches(final Class<T> cls, final Predicate<? super T> predicate, long... range) {
final List<T> allInstances = allInstances(cls, range);
final List<T> filtered = new ArrayList<T>();
for (final T instance : allInstances) {
if (predicate.apply(instance)) {
filtered.add(instance);
}
}
return filtered;
}
@Programmatic
@Override
public <T> List<T> allMatches(final Query<T> query) {
if(autoFlush) {
transactionService.flushTransaction();
}
return submitQuery(query);
}
<T> List<T> submitQuery(final Query<T> query) {
final List<ObjectAdapter> allMatching = persistenceSessionServiceInternal.allMatchingQuery(query);
return ObjectAdapter.Util.unwrapT(allMatching);
}
// //////////////////////////////////////
@Programmatic
@Override
public <T> T uniqueMatch(final Class<T> type, final Predicate<T> predicate) {
final List<T> instances = allMatches(type, predicate, 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 " + predicate);
}
return firstInstanceElseNull(instances);
}
@Programmatic
@Override
public <T> T uniqueMatch(final Query<T> query) {
final List<T> instances = allMatches(query); // No need to fetch more than 2.
if (instances.size() > 1) {
throw new RepositoryException("Found more that one instance for query:" + query.getDescription());
}
return firstInstanceElseNull(instances);
}
// //////////////////////////////////////
@Programmatic
@Override
public <T> T firstMatch(final Class<T> cls, final Predicate<T> predicate) {
final List<T> allInstances = allInstances(cls); // Have to fetch all, as matching is done in next loop
for (final T instance : allInstances) {
if (predicate.apply(instance)) {
return instance;
}
}
return null;
}
@Programmatic
@Override
@SuppressWarnings("unchecked")
public <T> T firstMatch(final Query<T> query) {
if(autoFlush) {
transactionService.flushTransaction();
}
final ObjectAdapter firstMatching = persistenceSessionServiceInternal.firstMatchingQuery(query);
return (T) ObjectAdapter.Util.unwrap(firstMatching);
}
// //////////////////////////////////////
private static <T> T firstInstanceElseNull(final List<T> instances) {
return instances.size() == 0 ? null : instances.get(0);
}
private Object unwrapped(Object domainObject) {
return wrapperFactory != null ? wrapperFactory.unwrap(domainObject) : domainObject;
}
@javax.inject.Inject
FactoryService factoryService;
@javax.inject.Inject
WrapperFactory wrapperFactory;
@javax.inject.Inject
TransactionService transactionService;
@javax.inject.Inject
PersistenceSessionServiceInternal persistenceSessionServiceInternal;
}