Return-Path: X-Original-To: apmail-incubator-isis-commits-archive@minotaur.apache.org Delivered-To: apmail-incubator-isis-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9E852D33E for ; Thu, 12 Jul 2012 15:06:13 +0000 (UTC) Received: (qmail 35956 invoked by uid 500); 12 Jul 2012 15:06:13 -0000 Delivered-To: apmail-incubator-isis-commits-archive@incubator.apache.org Received: (qmail 35913 invoked by uid 500); 12 Jul 2012 15:06:12 -0000 Mailing-List: contact isis-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: isis-dev@incubator.apache.org Delivered-To: mailing list isis-commits@incubator.apache.org Received: (qmail 35891 invoked by uid 99); 12 Jul 2012 15:06:11 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 12 Jul 2012 15:06:11 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED,T_FILL_THIS_FORM_SHORT X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 12 Jul 2012 15:06:07 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 2EFC3238890D; Thu, 12 Jul 2012 15:05:48 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1360713 [2/4] - in /incubator/isis/trunk/framework: runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/ runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apach... Date: Thu, 12 Jul 2012 15:05:46 -0000 To: isis-commits@incubator.apache.org From: danhaywood@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120712150548.2EFC3238890D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Copied: incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusObjectStore.java (from r1359418, incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/DataNucleusObjectStore.java) URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusObjectStore.java?p2=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusObjectStore.java&p1=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/DataNucleusObjectStore.java&r1=1359418&r2=1360713&rev=1360713&view=diff ============================================================================== --- incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/DataNucleusObjectStore.java (original) +++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusObjectStore.java Thu Jul 12 15:05:44 2012 @@ -1,771 +1,771 @@ -package org.apache.isis.runtimes.dflt.objectstores.datanucleus; - -import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg; -import static org.apache.isis.core.commons.ensure.Ensure.ensureThatContext; -import static org.apache.isis.core.commons.ensure.Ensure.ensureThatState; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; - -import java.sql.Connection; -import java.text.MessageFormat; -import java.util.List; -import java.util.Map; - -import javax.jdo.PersistenceManager; -import javax.jdo.Query; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import org.apache.log4j.Logger; - -import org.apache.isis.core.commons.config.ConfigurationConstants; -import org.apache.isis.core.commons.config.IsisConfiguration; -import org.apache.isis.core.commons.debug.DebugBuilder; -import org.apache.isis.core.commons.exceptions.NotYetImplementedException; -import org.apache.isis.core.metamodel.adapter.ObjectAdapter; -import org.apache.isis.core.metamodel.adapter.ObjectAdapterFactory; -import org.apache.isis.core.metamodel.adapter.ResolveState; -import org.apache.isis.core.metamodel.adapter.oid.AggregatedOid; -import org.apache.isis.core.metamodel.adapter.oid.Oid; -import org.apache.isis.core.metamodel.adapter.oid.RootOid; -import org.apache.isis.core.metamodel.adapter.oid.TypedOid; -import org.apache.isis.core.metamodel.spec.ObjectSpecId; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import org.apache.isis.core.metamodel.spec.SpecificationLoader; -import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware; -import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.commands.DataNucleusCreateObjectCommand; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.commands.DataNucleusDeleteObjectCommand; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.commands.DataNucleusUpdateObjectCommand; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.queries.PersistenceQueryFindAllInstancesProcessor; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.queries.PersistenceQueryFindByPatternProcessor; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.queries.PersistenceQueryFindByTitleProcessor; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.queries.PersistenceQueryFindUsingApplibQueryProcessor; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.queries.PersistenceQueryProcessor; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.queries.QueryUtil; -import org.apache.isis.runtimes.dflt.objectstores.jdo.metamodel.facets.object.query.JdoNamedQuery; -import org.apache.isis.runtimes.dflt.objectstores.jdo.metamodel.util.JdoPropertyUtils; -import org.apache.isis.runtimes.dflt.runtime.persistence.ObjectNotFoundException; -import org.apache.isis.runtimes.dflt.runtime.persistence.PersistenceSessionHydratorAware; -import org.apache.isis.runtimes.dflt.runtime.persistence.UnsupportedFindException; -import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.ObjectStore; -import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.CreateObjectCommand; -import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.DestroyObjectCommand; -import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.PersistenceCommand; -import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.SaveObjectCommand; -import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindAllInstances; -import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindByPattern; -import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindByTitle; -import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindUsingApplibQueryDefault; -import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext; -import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManager; -import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceQuery; -import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSessionHydrator; -import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransaction; -import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransactionManager; -import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransactionManagerAware; - -public class DataNucleusObjectStore implements ObjectStore, PersistenceSessionHydratorAware, SpecificationLoaderAware, IsisTransactionManagerAware { - - private static final Logger LOG = Logger.getLogger(DataNucleusObjectStore.class); - - static enum State { - NOT_YET_OPEN, OPEN, CLOSED; - } - - /** - * @see #isFixturesInstalled() - */ - public static final String IS_FIXTURES_INSTALLED_KEY = ConfigurationConstants.ROOT + "persistor.datanucleus.install-fixtures"; - public static final boolean IS_FIXTURES_INSTALLED_DEFAULT = true; - - static enum TransactionMode { - /** - * Requires transactions to be started explicitly. - */ - UNCHAINED, - /** - * Transactions are started automatically if not already in progress. - */ - CHAINED; - } - - private final IsisConfiguration configuration; - private final ObjectAdapterFactory adapterFactory; - private final AdapterManager adapterManager; - private final DataNucleusApplicationComponents applicationComponents; - - private final Map registeredServices = Maps.newHashMap(); - - private SpecificationLoader specificationLoader; - private PersistenceManager persistenceManager; - - private final Map, PersistenceQueryProcessor> persistenceQueryProcessorByClass = Maps.newHashMap(); - // private final LoadPostProcessor loadPostProcessor; - - private State state; - private TransactionMode transactionMode; - - private PersistenceSessionHydrator hydrator; - private IsisTransactionManager transactionManager; - - public DataNucleusObjectStore(IsisConfiguration configuration, ObjectAdapterFactory adapterFactory, AdapterManager adapterManager, DataNucleusApplicationComponents applicationComponents) { - ensureThatArg(configuration, is(notNullValue())); - ensureThatArg(adapterFactory, is(notNullValue())); - ensureThatArg(adapterManager, is(notNullValue())); - ensureThatArg(applicationComponents, is(notNullValue())); - - this.state = State.NOT_YET_OPEN; - this.transactionMode = TransactionMode.UNCHAINED; - - this.configuration = configuration; - this.adapterFactory = adapterFactory; - this.adapterManager = adapterManager; - this.applicationComponents = applicationComponents; - } - - @Override - public String name() { - return "datanucleus"; - } - - // /////////////////////////////////////////////////////////////////////// - // open, close - // /////////////////////////////////////////////////////////////////////// - - public void open() { - ensureNotYetOpen(); - ensureDependenciesInjected(); - - openSession(); - ensureThatState(persistenceManager, is(notNullValue())); - - addPersistenceQueryProcessors(persistenceManager); - - state = State.OPEN; - } - - /** - * {@inheritDoc} - *

- * Automatically {@link IsisTransactionManager#endTransaction() ends - * (commits)} the current (Isis) {@link Transaction}. This in turn - * {@link DataNucleusObjectStore#commitJpaTransaction() commits the underlying - * JPA transaction}. - *

- * The corresponding DataNucleus {@link Entity} is then - * {@link EntityManager#close() close}d. - */ - public void close() { - ensureOpened(); - ensureThatState(persistenceManager, is(notNullValue())); - - final IsisTransaction currentTransaction = getTransactionManager().getTransaction(); - if (currentTransaction != null && currentTransaction.getState().canCommit()) { - getTransactionManager().endTransaction(); - } - - persistenceManager.close(); - - state = State.CLOSED; - } - - private PersistenceManager openSession() { - this.persistenceManager = applicationComponents.createPersistenceManager(); - return this.persistenceManager; - } - - private void addPersistenceQueryProcessors(final PersistenceManager persistenceManager) { - persistenceQueryProcessorByClass.put(PersistenceQueryFindAllInstances.class, new PersistenceQueryFindAllInstancesProcessor(adapterManager, persistenceManager)); - persistenceQueryProcessorByClass.put(PersistenceQueryFindByTitle.class, new PersistenceQueryFindByTitleProcessor(adapterManager, persistenceManager)); - persistenceQueryProcessorByClass.put(PersistenceQueryFindByPattern.class, new PersistenceQueryFindByPatternProcessor(adapterManager, persistenceManager)); - persistenceQueryProcessorByClass.put(PersistenceQueryFindUsingApplibQueryDefault.class, new PersistenceQueryFindUsingApplibQueryProcessor(adapterManager, persistenceManager)); - } - - // /////////////////////////////////////////////////////////////////////// - // isFixturesInstalled - // /////////////////////////////////////////////////////////////////////// - - /** - * Implementation looks for the {@link #IS_FIXTURES_INSTALLED_KEY} in the - * {@link #getConfiguration() configuration}. - *

- * By default this is not expected to be there, but utilities can add in on - * the fly during bootstrapping if required. - */ - public boolean isFixturesInstalled() { - return getConfiguration().getBoolean(IS_FIXTURES_INSTALLED_KEY, IS_FIXTURES_INSTALLED_DEFAULT); - } - - - // /////////////////////////////////////////////////////////////////////// - // reset - // /////////////////////////////////////////////////////////////////////// - - public void reset() { - // does nothing. - } - - /** - * Non-API. - */ - public Connection getJavaSqlConnection() { - return (Connection) persistenceManager.getDataStoreConnection().getNativeConnection(); - } - - // /////////////////////////////////////////////////////////////////////// - // TransactionMode (not API) - // /////////////////////////////////////////////////////////////////////// - - public TransactionMode getTransactionMode() { - return transactionMode; - } - - public void setTransactionMode(final TransactionMode transactionMode) { - ensureNotInTransaction(); - this.transactionMode = transactionMode; - } - - // /////////////////////////////////////////////////////////////////////// - // Transactions - // /////////////////////////////////////////////////////////////////////// - - public void startTransaction() { - beginJpaTransaction(); - } - - public void endTransaction() { - commitJpaTransaction(); - } - - public void abortTransaction() { - rollbackJpaTransaction(); - } - - private void beginJpaTransaction() { - final javax.jdo.Transaction transaction = getPersistenceManager().currentTransaction(); - if (transaction.isActive()) { - throw new IllegalStateException("Transaction already active"); - } - transaction.begin(); - } - - private void commitJpaTransaction() { - final javax.jdo.Transaction transaction = getPersistenceManager().currentTransaction(); - if (transaction.isActive()) { - transaction.commit(); - } - } - - private void rollbackJpaTransaction() { - final javax.jdo.Transaction transaction = getPersistenceManager().currentTransaction(); - if (transaction.isActive()) { - transaction.rollback(); - } - } - - // /////////////////////////////////////////////////////////////////////// - // Command Factory - // /////////////////////////////////////////////////////////////////////// - - public CreateObjectCommand createCreateObjectCommand(final ObjectAdapter adapter) { - ensureOpened(); - ensureInSession(); - - if (LOG.isDebugEnabled()) { - LOG.debug("create object - creating command for: " + adapter); - } - if (adapter.representsPersistent()) { - throw new IllegalArgumentException("Adapter is persistent; adapter: " + adapter); - } - return new DataNucleusCreateObjectCommand(adapter, getPersistenceManager()); - } - - public SaveObjectCommand createSaveObjectCommand(final ObjectAdapter adapter) { - ensureOpened(); - ensureInSession(); - - if (!adapter.representsPersistent()) { - throw new IllegalArgumentException("Adapter is not persistent; adapter: " + adapter); - } - if (LOG.isDebugEnabled()) { - LOG.debug("save object - creating command for: " + adapter); - } - return new DataNucleusUpdateObjectCommand(adapter, getPersistenceManager()); - } - - public DestroyObjectCommand createDestroyObjectCommand(final ObjectAdapter adapter) { - ensureOpened(); - ensureInSession(); - - if (LOG.isDebugEnabled()) { - LOG.debug("destroy object - creating command for: " + adapter); - } - if (!adapter.representsPersistent()) { - throw new IllegalArgumentException("Adapter is not persistent; adapter: " + adapter); - } - return new DataNucleusDeleteObjectCommand(adapter, getPersistenceManager()); - } - - // /////////////////////////////////////////////////////////////////////// - // Execute - // /////////////////////////////////////////////////////////////////////// - - public void execute(final List commands) { - ensureOpened(); - ensureInTransaction(); - - if (LOG.isDebugEnabled()) { - LOG.debug("execute " + commands.size() + " commands"); - } - - if (commands.size() <= 0) { - if (LOG.isDebugEnabled()) { - LOG.debug("no commands"); - } - return; - } - - executeCommands(commands); - } - - private void executeCommands(final List commands) { - try { - for (final PersistenceCommand command : commands) { - command.execute(null); - } - getPersistenceManager().flush(); - } catch (final RuntimeException e) { - LOG.warn("Failure during execution", e); - throw e; - } - } - - // /////////////////////////////////////////////////////////////////////// - // getObject, resolveImmediately, resolveField - // /////////////////////////////////////////////////////////////////////// - - public ObjectAdapter getObject(final TypedOid oid) { - ensureOpened(); - ensureInTransaction(); - - if (LOG.isDebugEnabled()) { - LOG.debug("getObject; oid=" + oid); - } - - if(oid instanceof AggregatedOid) { - // does it make sense to get these directly? not sure, so for now have decided to fail fast. - throw new UnsupportedOperationException("Cannot retrieve aggregated objects directly, oid: " + oid.enString()); - } - final RootOid rootOid = (RootOid) oid; - Object result = null; - try { - final Class cls = clsOf(rootOid); - final Object idPropValue = idValueOf(rootOid); - result = getPersistenceManager().getObjectById(cls, idPropValue); - } catch (final RuntimeException e) { - throw e; - } - - if (result == null) { - throw new ObjectNotFoundException(oid); - } - final ObjectAdapter adapter = hydrator.recreateAdapter(oid, result); - - //TODO: loadPostProcessor.loaded(adapter); - return adapter; - } - - /** - * Will do nothing if object is already resolved or if object is transient. - *

- * TODO: - * The final {@link ResolveState} of the adapter is set using - * {@link NakedLoadPostEventListener#onPostLoad(org.hibernate.event.PostLoadEvent)} - * Note: this is the same behaviour as MemoryObjectStore, XmlObjectStore - * and HibernateObjectStore. - *

- * REVIEW: if the initial state is RESOLVING_PART, then the - * {@link ResolveState} is not changed. Is this right? - */ - public void resolveImmediately(final ObjectAdapter adapter) { - ensureOpened(); - ensureInTransaction(); - - if (LOG.isDebugEnabled()) { - LOG.debug("resolveImmediately; oid=" + adapter.getOid().enString()); - } - - if (adapter.isResolved()) { - if (LOG.isDebugEnabled()) { - LOG.debug("; already resolved - ignoring"); - } - return; - } - if (!adapter.representsPersistent()) { - if (LOG.isDebugEnabled()) { - LOG.debug("; not persistent - ignoring"); - } - return; - } - - final Oid oid = adapter.getOid(); - if (oid instanceof AggregatedOid) { - if (LOG.isDebugEnabled()) { - LOG.debug("; aggregated - resolving parent"); - } - final AggregatedOid aggregatedOid = (AggregatedOid) oid; - final TypedOid parentOid = aggregatedOid.getParentOid(); - final ObjectAdapter parentAdapter = this.getObject(parentOid); - resolveImmediately(parentAdapter); - return; - } - - JdoPropertyUtils.setPropertyIdFromOid(adapter, getAdapterFactory()); - try { - final Object domainObject = adapter.getObject(); - - getPersistenceManager().refresh(domainObject); - } catch (final RuntimeException e) { - throw new ObjectNotFoundException(adapter.getOid(), e); - } - - if (adapter.getObject() == null) { - throw new ObjectNotFoundException(adapter.getOid()); - } - - // possibly redundant because also called in the post-load event - // listener, - // but is required if we were ever to get an eager left-outer-join as - // the result of a refresh (sounds possible). - - //TODO: loadPostProcessor.loaded(adapter); - } - - /** - * Walking the graph. - */ - public void resolveField(final ObjectAdapter object, final ObjectAssociation association) { - ensureOpened(); - ensureInTransaction(); - - final ObjectAdapter referencedCollectionAdapter = association.get(object); - - // if a proxy collection, then force it to initialize. - if (association.isOneToManyAssociation()) { - ensureThatState(referencedCollectionAdapter, is(notNullValue())); - - final Object referencedCollection = referencedCollectionAdapter.getObject(); - ensureThatState(referencedCollection, is(notNullValue())); - - // just 'touching' the object is sufficient. - referencedCollection.hashCode(); - } - - if (referencedCollectionAdapter != null) { - // this works and seems to be sufficient (is also called from - // NakedPostLoadEventListener for direct retrievals rather than - // walking the - // graph). - - // TODO: loadPostProcessor.loaded(referencedCollectionAdapter); - } - } - - // /////////////////////////////////////////////////////////////////////// - // getInstances, hasInstances - // /////////////////////////////////////////////////////////////////////// - - public List getInstances(final PersistenceQuery persistenceQuery) { - ensureOpened(); - ensureInTransaction(); - - final PersistenceQueryProcessor processor = persistenceQueryProcessorByClass.get(persistenceQuery.getClass()); - if (processor == null) { - throw new UnsupportedFindException(MessageFormat.format("Unsupported criteria type: {0}", persistenceQuery.getClass().getName())); - } - return processPersistenceQuery(processor, persistenceQuery); - } - - @SuppressWarnings("unchecked") - private List processPersistenceQuery(final PersistenceQueryProcessor persistenceQueryProcessor, final PersistenceQuery persistenceQuery) { - return persistenceQueryProcessor.process((Q)persistenceQuery); - } - - public boolean hasInstances(final ObjectSpecification specification) { - ensureOpened(); - ensureInTransaction(); - - if (LOG.isDebugEnabled()) { - LOG.debug("hasInstances: class=" + specification.getFullIdentifier()); - } - - if (!specification.persistability().isPersistable()) { - LOG.warn("hasInstances: trying to run for non-persistent class " + specification); - return false; - } - - final Query query = QueryUtil.createQuery(getPersistenceManager(), "o", "select o.id", specification, null); - throw new NotYetImplementedException(); - //query.set.setMaxResults(1); - //return !query.getResultList().isEmpty(); - } - - // /////////////////////////////////////////////////////////////////////// - // Helpers (loadObjects) - // /////////////////////////////////////////////////////////////////////// - - @SuppressWarnings("unused") - private List loadObjects(final ObjectSpecification specification, final List listOfPojs, final AdapterManager adapterManager) { - final List adapters = Lists.newArrayList(); - int i = 0; - for (final Object pojo : listOfPojs) { - // REVIEW: cannot just load adapter for object - if Naked Objects - // has - // already loaded the object - // then object won't match it (e.g. if getInstances has been called - // and an instance has - // been loaded) - so need to use Hibernate session to get an Oid to - // do a lookup in that case - adapters.add(adapterManager.getAdapterFor(pojo)); - } - return adapters; - } - - // /////////////////////////////////////////////////////////////////////// - // Services - // /////////////////////////////////////////////////////////////////////// - - @Override - public void registerService(RootOid rootOid) { - ensureOpened(); - this.registeredServices.put(rootOid.getObjectSpecId(), rootOid); - } - - @Override - public RootOid getOidForService(ObjectSpecification serviceSpec) { - ensureOpened(); - return this.registeredServices.get(serviceSpec.getSpecId()); - } - - // /////////////////////////////////////////////////////////////////////// - // Helpers: ensure* - // /////////////////////////////////////////////////////////////////////// - - private void ensureNotYetOpen() { - ensureStateIs(State.NOT_YET_OPEN); - } - - private void ensureOpened() { - ensureStateIs(State.OPEN); - } - - private void ensureInSession() { - ensureThatContext(IsisContext.inSession(), is(true)); - } - - private void ensureNotInTransaction() { - ensureInSession(); - ensureThatContext(IsisContext.inTransaction(), is(false)); - } - - private void ensureInTransaction() { - if (transactionMode == TransactionMode.UNCHAINED) { - ensureThatContext(IsisContext.inTransaction(), is(true)); - ensureInHibernateTransaction(); - } else { - ensureInSession(); - if (IsisContext.inTransaction()) { - ensureInHibernateTransaction(); - } else { - getTransactionManager().startTransaction(); - } - } - } - - private void ensureInHibernateTransaction() { - javax.jdo.Transaction currentTransaction = getPersistenceManager().currentTransaction(); - ensureThatState(currentTransaction, is(notNullValue())); - ensureThatState(currentTransaction.isActive(), is(true)); - } - - private void ensureStateIs(final State stateRequired) { - if (state == stateRequired) { - return; - } - throw new IllegalStateException("State is: " + state + "; should be: " + stateRequired); - } - - // /////////////////////////////////////////////////////////////////////// - // Debugging - // /////////////////////////////////////////////////////////////////////// - - public void debugData(final DebugBuilder debug) { - throw new NotYetImplementedException(); - } - - public String debugTitle() { - throw new NotYetImplementedException(); - } - - // /////////////////////////////////////////////////////////////////////// - // non-API - // /////////////////////////////////////////////////////////////////////// - - public JdoNamedQuery getNamedQuery(String queryName) { - return applicationComponents.getNamedQuery(queryName); - } - - /** - * For testing purposes, to allow fixtures to use JDO to initialize the - * database without triggering the objectstore. - * - * @see #resumeListener() - */ - public void suspendListener() { - applicationComponents.suspendListener(); - } - - /** - * For testing purposes, to allow fixtures to use JDO to initialize the - * database without triggering the objectstore. - * - * @see #suspendListener() - */ - public void resumeListener() { - applicationComponents.resumeListener(); - } - - - // /////////////////////////////////////////////////////////////////////// - // Helpers - // /////////////////////////////////////////////////////////////////////// - - private Class clsOf(final TypedOid oid) { - final ObjectSpecification objectSpec = getSpecificationLoader().lookupBySpecId(oid.getObjectSpecId()); - return objectSpec.getCorrespondingClass(); - } - - private Object idValueOf(final RootOid oid) { - final ObjectSpecification objectSpec = getSpecificationLoader().lookupBySpecId(oid.getObjectSpecId()); - return JdoPropertyUtils.idValueOf(oid, objectSpec); - } - - - /** - * Intended for internal and test use only. - */ - public PersistenceManager getPersistenceManager() { - return persistenceManager; - } - - // /////////////////////////////////////////////////////////////////////// - // Dependencies (from constructor) - // /////////////////////////////////////////////////////////////////////// - - public IsisConfiguration getConfiguration() { - return configuration; - } - - public ObjectAdapterFactory getAdapterFactory() { - return adapterFactory; - } - - /** - * @see #setAdapterManager(AdapterManager) - */ - public AdapterManager getAdapterManager() { - return adapterManager; - } - - // /////////////////////////////////////////////////////////////////////// - // Dependencies (injected) - // /////////////////////////////////////////////////////////////////////// - - private void ensureDependenciesInjected() { - ensureThatState(specificationLoader, is(notNullValue())); - ensureThatState(adapterManager, is(notNullValue())); - ensureThatState(hydrator, is(notNullValue())); - //TODO: ensureThatState(hibernateApplicationComponents, is(notNullValue())); - ensureThatState(transactionManager, is(notNullValue())); - } - - /** - * @see #setSpecificationLoader(SpecificationLoader) - */ - public SpecificationLoader getSpecificationLoader() { - return specificationLoader; - } - - /** - * Injected prior to {@link #open()}ing. - *

- * Injected by owning {@link PersistenceSessionObjectStore} (see - * {@link PersistenceSessionObjectStore#open()}) by virtue of fact that this - * implementation is {@link SpecificationLoaderAware aware} of the - * {@link SpecificationLoader}. - */ - public void setSpecificationLoader(final SpecificationLoader specificationLoader) { - this.specificationLoader = specificationLoader; - } - - /** - * @see #setHydrator(PersistenceSessionHydrator) - */ - public PersistenceSessionHydrator getHydrator() { - return hydrator; - } - - /** - * Injected prior to {@link #open()}ing. - *

- * Injected by owning {@link PersistenceSessionObjectStore} (see - * {@link PersistenceSessionObjectStore#open()}) by virtue of fact that this - * implementation is {@link PersistenceSessionHydratorAware aware} of the - * {@link PersistenceSessionHydrator}. - */ - public void setHydrator(final PersistenceSessionHydrator hydrator) { - this.hydrator = hydrator; - } - - /** - * @see #getTransactionManager() - */ - public IsisTransactionManager getTransactionManager() { - return transactionManager; - } - - /** - * Injected prior to {@link #open()}ing. - *

- * Injected by owning {@link PersistenceSessionObjectStore} (see - * {@link PersistenceSessionObjectStore#open()}) by virtue of fact that this - * implementation is {@link NakedObjectTransactionManagerAware aware} of the - * {@link NakedObjectTransactionManager}. - */ - public void setTransactionManager(final IsisTransactionManager transactionManager) { - this.transactionManager = transactionManager; - } - - - - -// /** -// * @see #setHibernateApplicationComponents(HibernateMetaDataComponents) -// */ -// public HibernateApplicationComponents getHibernateApplicationComponents() { -// return hibernateApplicationComponents; -// } -// -// /** -// * Injected prior to {@link #open()}ing. -// *

-// * Injected by owning {@link JpaPersistenceSession} (see -// * {@link JpaPersistenceSession#doOpen()); hard-coded into implementation. -// */ -// public void setHibernateApplicationComponents(final HibernateApplicationComponents hibernateApplicationComponents) { -// this.hibernateApplicationComponents = hibernateApplicationComponents; -// } - -} +package org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus; + +import static org.apache.isis.core.commons.ensure.Ensure.ensureThatArg; +import static org.apache.isis.core.commons.ensure.Ensure.ensureThatContext; +import static org.apache.isis.core.commons.ensure.Ensure.ensureThatState; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; + +import java.sql.Connection; +import java.text.MessageFormat; +import java.util.List; +import java.util.Map; + +import javax.jdo.PersistenceManager; +import javax.jdo.Query; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.apache.log4j.Logger; + +import org.apache.isis.core.commons.config.ConfigurationConstants; +import org.apache.isis.core.commons.config.IsisConfiguration; +import org.apache.isis.core.commons.debug.DebugBuilder; +import org.apache.isis.core.commons.exceptions.NotYetImplementedException; +import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterFactory; +import org.apache.isis.core.metamodel.adapter.ResolveState; +import org.apache.isis.core.metamodel.adapter.oid.AggregatedOid; +import org.apache.isis.core.metamodel.adapter.oid.Oid; +import org.apache.isis.core.metamodel.adapter.oid.RootOid; +import org.apache.isis.core.metamodel.adapter.oid.TypedOid; +import org.apache.isis.core.metamodel.spec.ObjectSpecId; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import org.apache.isis.core.metamodel.spec.SpecificationLoader; +import org.apache.isis.core.metamodel.spec.SpecificationLoaderAware; +import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.commands.DataNucleusCreateObjectCommand; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.commands.DataNucleusDeleteObjectCommand; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.commands.DataNucleusUpdateObjectCommand; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.queries.PersistenceQueryFindAllInstancesProcessor; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.queries.PersistenceQueryFindByPatternProcessor; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.queries.PersistenceQueryFindByTitleProcessor; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.queries.PersistenceQueryFindUsingApplibQueryProcessor; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.queries.PersistenceQueryProcessor; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.queries.QueryUtil; +import org.apache.isis.runtimes.dflt.objectstores.jdo.metamodel.facets.object.query.JdoNamedQuery; +import org.apache.isis.runtimes.dflt.objectstores.jdo.metamodel.util.JdoPropertyUtils; +import org.apache.isis.runtimes.dflt.runtime.persistence.ObjectNotFoundException; +import org.apache.isis.runtimes.dflt.runtime.persistence.PersistenceSessionHydratorAware; +import org.apache.isis.runtimes.dflt.runtime.persistence.UnsupportedFindException; +import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.ObjectStore; +import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.CreateObjectCommand; +import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.DestroyObjectCommand; +import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.PersistenceCommand; +import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.transaction.SaveObjectCommand; +import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindAllInstances; +import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindByPattern; +import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindByTitle; +import org.apache.isis.runtimes.dflt.runtime.persistence.query.PersistenceQueryFindUsingApplibQueryDefault; +import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext; +import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManager; +import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceQuery; +import org.apache.isis.runtimes.dflt.runtime.system.persistence.PersistenceSessionHydrator; +import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransaction; +import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransactionManager; +import org.apache.isis.runtimes.dflt.runtime.system.transaction.IsisTransactionManagerAware; + +public class DataNucleusObjectStore implements ObjectStore, PersistenceSessionHydratorAware, SpecificationLoaderAware, IsisTransactionManagerAware { + + private static final Logger LOG = Logger.getLogger(DataNucleusObjectStore.class); + + static enum State { + NOT_YET_OPEN, OPEN, CLOSED; + } + + /** + * @see #isFixturesInstalled() + */ + public static final String IS_FIXTURES_INSTALLED_KEY = ConfigurationConstants.ROOT + "persistor.datanucleus.install-fixtures"; + public static final boolean IS_FIXTURES_INSTALLED_DEFAULT = true; + + static enum TransactionMode { + /** + * Requires transactions to be started explicitly. + */ + UNCHAINED, + /** + * Transactions are started automatically if not already in progress. + */ + CHAINED; + } + + private final IsisConfiguration configuration; + private final ObjectAdapterFactory adapterFactory; + private final AdapterManager adapterManager; + private final DataNucleusApplicationComponents applicationComponents; + + private final Map registeredServices = Maps.newHashMap(); + + private SpecificationLoader specificationLoader; + private PersistenceManager persistenceManager; + + private final Map, PersistenceQueryProcessor> persistenceQueryProcessorByClass = Maps.newHashMap(); + // private final LoadPostProcessor loadPostProcessor; + + private State state; + private TransactionMode transactionMode; + + private PersistenceSessionHydrator hydrator; + private IsisTransactionManager transactionManager; + + public DataNucleusObjectStore(IsisConfiguration configuration, ObjectAdapterFactory adapterFactory, AdapterManager adapterManager, DataNucleusApplicationComponents applicationComponents) { + ensureThatArg(configuration, is(notNullValue())); + ensureThatArg(adapterFactory, is(notNullValue())); + ensureThatArg(adapterManager, is(notNullValue())); + ensureThatArg(applicationComponents, is(notNullValue())); + + this.state = State.NOT_YET_OPEN; + this.transactionMode = TransactionMode.UNCHAINED; + + this.configuration = configuration; + this.adapterFactory = adapterFactory; + this.adapterManager = adapterManager; + this.applicationComponents = applicationComponents; + } + + @Override + public String name() { + return "datanucleus"; + } + + // /////////////////////////////////////////////////////////////////////// + // open, close + // /////////////////////////////////////////////////////////////////////// + + public void open() { + ensureNotYetOpen(); + ensureDependenciesInjected(); + + openSession(); + ensureThatState(persistenceManager, is(notNullValue())); + + addPersistenceQueryProcessors(persistenceManager); + + state = State.OPEN; + } + + /** + * {@inheritDoc} + *

+ * Automatically {@link IsisTransactionManager#endTransaction() ends + * (commits)} the current (Isis) {@link Transaction}. This in turn + * {@link DataNucleusObjectStore#commitJpaTransaction() commits the underlying + * JPA transaction}. + *

+ * The corresponding DataNucleus {@link Entity} is then + * {@link EntityManager#close() close}d. + */ + public void close() { + ensureOpened(); + ensureThatState(persistenceManager, is(notNullValue())); + + final IsisTransaction currentTransaction = getTransactionManager().getTransaction(); + if (currentTransaction != null && currentTransaction.getState().canCommit()) { + getTransactionManager().endTransaction(); + } + + persistenceManager.close(); + + state = State.CLOSED; + } + + private PersistenceManager openSession() { + this.persistenceManager = applicationComponents.createPersistenceManager(); + return this.persistenceManager; + } + + private void addPersistenceQueryProcessors(final PersistenceManager persistenceManager) { + persistenceQueryProcessorByClass.put(PersistenceQueryFindAllInstances.class, new PersistenceQueryFindAllInstancesProcessor(adapterManager, persistenceManager)); + persistenceQueryProcessorByClass.put(PersistenceQueryFindByTitle.class, new PersistenceQueryFindByTitleProcessor(adapterManager, persistenceManager)); + persistenceQueryProcessorByClass.put(PersistenceQueryFindByPattern.class, new PersistenceQueryFindByPatternProcessor(adapterManager, persistenceManager)); + persistenceQueryProcessorByClass.put(PersistenceQueryFindUsingApplibQueryDefault.class, new PersistenceQueryFindUsingApplibQueryProcessor(adapterManager, persistenceManager)); + } + + // /////////////////////////////////////////////////////////////////////// + // isFixturesInstalled + // /////////////////////////////////////////////////////////////////////// + + /** + * Implementation looks for the {@link #IS_FIXTURES_INSTALLED_KEY} in the + * {@link #getConfiguration() configuration}. + *

+ * By default this is not expected to be there, but utilities can add in on + * the fly during bootstrapping if required. + */ + public boolean isFixturesInstalled() { + return getConfiguration().getBoolean(IS_FIXTURES_INSTALLED_KEY, IS_FIXTURES_INSTALLED_DEFAULT); + } + + + // /////////////////////////////////////////////////////////////////////// + // reset + // /////////////////////////////////////////////////////////////////////// + + public void reset() { + // does nothing. + } + + /** + * Non-API. + */ + public Connection getJavaSqlConnection() { + return (Connection) persistenceManager.getDataStoreConnection().getNativeConnection(); + } + + // /////////////////////////////////////////////////////////////////////// + // TransactionMode (not API) + // /////////////////////////////////////////////////////////////////////// + + public TransactionMode getTransactionMode() { + return transactionMode; + } + + public void setTransactionMode(final TransactionMode transactionMode) { + ensureNotInTransaction(); + this.transactionMode = transactionMode; + } + + // /////////////////////////////////////////////////////////////////////// + // Transactions + // /////////////////////////////////////////////////////////////////////// + + public void startTransaction() { + beginJpaTransaction(); + } + + public void endTransaction() { + commitJpaTransaction(); + } + + public void abortTransaction() { + rollbackJpaTransaction(); + } + + private void beginJpaTransaction() { + final javax.jdo.Transaction transaction = getPersistenceManager().currentTransaction(); + if (transaction.isActive()) { + throw new IllegalStateException("Transaction already active"); + } + transaction.begin(); + } + + private void commitJpaTransaction() { + final javax.jdo.Transaction transaction = getPersistenceManager().currentTransaction(); + if (transaction.isActive()) { + transaction.commit(); + } + } + + private void rollbackJpaTransaction() { + final javax.jdo.Transaction transaction = getPersistenceManager().currentTransaction(); + if (transaction.isActive()) { + transaction.rollback(); + } + } + + // /////////////////////////////////////////////////////////////////////// + // Command Factory + // /////////////////////////////////////////////////////////////////////// + + public CreateObjectCommand createCreateObjectCommand(final ObjectAdapter adapter) { + ensureOpened(); + ensureInSession(); + + if (LOG.isDebugEnabled()) { + LOG.debug("create object - creating command for: " + adapter); + } + if (adapter.representsPersistent()) { + throw new IllegalArgumentException("Adapter is persistent; adapter: " + adapter); + } + return new DataNucleusCreateObjectCommand(adapter, getPersistenceManager()); + } + + public SaveObjectCommand createSaveObjectCommand(final ObjectAdapter adapter) { + ensureOpened(); + ensureInSession(); + + if (!adapter.representsPersistent()) { + throw new IllegalArgumentException("Adapter is not persistent; adapter: " + adapter); + } + if (LOG.isDebugEnabled()) { + LOG.debug("save object - creating command for: " + adapter); + } + return new DataNucleusUpdateObjectCommand(adapter, getPersistenceManager()); + } + + public DestroyObjectCommand createDestroyObjectCommand(final ObjectAdapter adapter) { + ensureOpened(); + ensureInSession(); + + if (LOG.isDebugEnabled()) { + LOG.debug("destroy object - creating command for: " + adapter); + } + if (!adapter.representsPersistent()) { + throw new IllegalArgumentException("Adapter is not persistent; adapter: " + adapter); + } + return new DataNucleusDeleteObjectCommand(adapter, getPersistenceManager()); + } + + // /////////////////////////////////////////////////////////////////////// + // Execute + // /////////////////////////////////////////////////////////////////////// + + public void execute(final List commands) { + ensureOpened(); + ensureInTransaction(); + + if (LOG.isDebugEnabled()) { + LOG.debug("execute " + commands.size() + " commands"); + } + + if (commands.size() <= 0) { + if (LOG.isDebugEnabled()) { + LOG.debug("no commands"); + } + return; + } + + executeCommands(commands); + } + + private void executeCommands(final List commands) { + try { + for (final PersistenceCommand command : commands) { + command.execute(null); + } + getPersistenceManager().flush(); + } catch (final RuntimeException e) { + LOG.warn("Failure during execution", e); + throw e; + } + } + + // /////////////////////////////////////////////////////////////////////// + // getObject, resolveImmediately, resolveField + // /////////////////////////////////////////////////////////////////////// + + public ObjectAdapter getObject(final TypedOid oid) { + ensureOpened(); + ensureInTransaction(); + + if (LOG.isDebugEnabled()) { + LOG.debug("getObject; oid=" + oid); + } + + if(oid instanceof AggregatedOid) { + // does it make sense to get these directly? not sure, so for now have decided to fail fast. + throw new UnsupportedOperationException("Cannot retrieve aggregated objects directly, oid: " + oid.enString()); + } + final RootOid rootOid = (RootOid) oid; + Object result = null; + try { + final Class cls = clsOf(rootOid); + final Object idPropValue = idValueOf(rootOid); + result = getPersistenceManager().getObjectById(cls, idPropValue); + } catch (final RuntimeException e) { + throw e; + } + + if (result == null) { + throw new ObjectNotFoundException(oid); + } + final ObjectAdapter adapter = hydrator.recreateAdapter(oid, result); + + //TODO: loadPostProcessor.loaded(adapter); + return adapter; + } + + /** + * Will do nothing if object is already resolved or if object is transient. + *

+ * TODO: + * The final {@link ResolveState} of the adapter is set using + * {@link NakedLoadPostEventListener#onPostLoad(org.hibernate.event.PostLoadEvent)} + * Note: this is the same behaviour as MemoryObjectStore, XmlObjectStore + * and HibernateObjectStore. + *

+ * REVIEW: if the initial state is RESOLVING_PART, then the + * {@link ResolveState} is not changed. Is this right? + */ + public void resolveImmediately(final ObjectAdapter adapter) { + ensureOpened(); + ensureInTransaction(); + + if (LOG.isDebugEnabled()) { + LOG.debug("resolveImmediately; oid=" + adapter.getOid().enString()); + } + + if (adapter.isResolved()) { + if (LOG.isDebugEnabled()) { + LOG.debug("; already resolved - ignoring"); + } + return; + } + if (!adapter.representsPersistent()) { + if (LOG.isDebugEnabled()) { + LOG.debug("; not persistent - ignoring"); + } + return; + } + + final Oid oid = adapter.getOid(); + if (oid instanceof AggregatedOid) { + if (LOG.isDebugEnabled()) { + LOG.debug("; aggregated - resolving parent"); + } + final AggregatedOid aggregatedOid = (AggregatedOid) oid; + final TypedOid parentOid = aggregatedOid.getParentOid(); + final ObjectAdapter parentAdapter = this.getObject(parentOid); + resolveImmediately(parentAdapter); + return; + } + + JdoPropertyUtils.setPropertyIdFromOid(adapter, getAdapterFactory()); + try { + final Object domainObject = adapter.getObject(); + + getPersistenceManager().refresh(domainObject); + } catch (final RuntimeException e) { + throw new ObjectNotFoundException(adapter.getOid(), e); + } + + if (adapter.getObject() == null) { + throw new ObjectNotFoundException(adapter.getOid()); + } + + // possibly redundant because also called in the post-load event + // listener, + // but is required if we were ever to get an eager left-outer-join as + // the result of a refresh (sounds possible). + + //TODO: loadPostProcessor.loaded(adapter); + } + + /** + * Walking the graph. + */ + public void resolveField(final ObjectAdapter object, final ObjectAssociation association) { + ensureOpened(); + ensureInTransaction(); + + final ObjectAdapter referencedCollectionAdapter = association.get(object); + + // if a proxy collection, then force it to initialize. + if (association.isOneToManyAssociation()) { + ensureThatState(referencedCollectionAdapter, is(notNullValue())); + + final Object referencedCollection = referencedCollectionAdapter.getObject(); + ensureThatState(referencedCollection, is(notNullValue())); + + // just 'touching' the object is sufficient. + referencedCollection.hashCode(); + } + + if (referencedCollectionAdapter != null) { + // this works and seems to be sufficient (is also called from + // NakedPostLoadEventListener for direct retrievals rather than + // walking the + // graph). + + // TODO: loadPostProcessor.loaded(referencedCollectionAdapter); + } + } + + // /////////////////////////////////////////////////////////////////////// + // getInstances, hasInstances + // /////////////////////////////////////////////////////////////////////// + + public List getInstances(final PersistenceQuery persistenceQuery) { + ensureOpened(); + ensureInTransaction(); + + final PersistenceQueryProcessor processor = persistenceQueryProcessorByClass.get(persistenceQuery.getClass()); + if (processor == null) { + throw new UnsupportedFindException(MessageFormat.format("Unsupported criteria type: {0}", persistenceQuery.getClass().getName())); + } + return processPersistenceQuery(processor, persistenceQuery); + } + + @SuppressWarnings("unchecked") + private List processPersistenceQuery(final PersistenceQueryProcessor persistenceQueryProcessor, final PersistenceQuery persistenceQuery) { + return persistenceQueryProcessor.process((Q)persistenceQuery); + } + + public boolean hasInstances(final ObjectSpecification specification) { + ensureOpened(); + ensureInTransaction(); + + if (LOG.isDebugEnabled()) { + LOG.debug("hasInstances: class=" + specification.getFullIdentifier()); + } + + if (!specification.persistability().isPersistable()) { + LOG.warn("hasInstances: trying to run for non-persistent class " + specification); + return false; + } + + final Query query = QueryUtil.createQuery(getPersistenceManager(), "o", "select o.id", specification, null); + throw new NotYetImplementedException(); + //query.set.setMaxResults(1); + //return !query.getResultList().isEmpty(); + } + + // /////////////////////////////////////////////////////////////////////// + // Helpers (loadObjects) + // /////////////////////////////////////////////////////////////////////// + + @SuppressWarnings("unused") + private List loadObjects(final ObjectSpecification specification, final List listOfPojs, final AdapterManager adapterManager) { + final List adapters = Lists.newArrayList(); + int i = 0; + for (final Object pojo : listOfPojs) { + // REVIEW: cannot just load adapter for object - if Naked Objects + // has + // already loaded the object + // then object won't match it (e.g. if getInstances has been called + // and an instance has + // been loaded) - so need to use Hibernate session to get an Oid to + // do a lookup in that case + adapters.add(adapterManager.getAdapterFor(pojo)); + } + return adapters; + } + + // /////////////////////////////////////////////////////////////////////// + // Services + // /////////////////////////////////////////////////////////////////////// + + @Override + public void registerService(RootOid rootOid) { + ensureOpened(); + this.registeredServices.put(rootOid.getObjectSpecId(), rootOid); + } + + @Override + public RootOid getOidForService(ObjectSpecification serviceSpec) { + ensureOpened(); + return this.registeredServices.get(serviceSpec.getSpecId()); + } + + // /////////////////////////////////////////////////////////////////////// + // Helpers: ensure* + // /////////////////////////////////////////////////////////////////////// + + private void ensureNotYetOpen() { + ensureStateIs(State.NOT_YET_OPEN); + } + + private void ensureOpened() { + ensureStateIs(State.OPEN); + } + + private void ensureInSession() { + ensureThatContext(IsisContext.inSession(), is(true)); + } + + private void ensureNotInTransaction() { + ensureInSession(); + ensureThatContext(IsisContext.inTransaction(), is(false)); + } + + private void ensureInTransaction() { + if (transactionMode == TransactionMode.UNCHAINED) { + ensureThatContext(IsisContext.inTransaction(), is(true)); + ensureInHibernateTransaction(); + } else { + ensureInSession(); + if (IsisContext.inTransaction()) { + ensureInHibernateTransaction(); + } else { + getTransactionManager().startTransaction(); + } + } + } + + private void ensureInHibernateTransaction() { + javax.jdo.Transaction currentTransaction = getPersistenceManager().currentTransaction(); + ensureThatState(currentTransaction, is(notNullValue())); + ensureThatState(currentTransaction.isActive(), is(true)); + } + + private void ensureStateIs(final State stateRequired) { + if (state == stateRequired) { + return; + } + throw new IllegalStateException("State is: " + state + "; should be: " + stateRequired); + } + + // /////////////////////////////////////////////////////////////////////// + // Debugging + // /////////////////////////////////////////////////////////////////////// + + public void debugData(final DebugBuilder debug) { + throw new NotYetImplementedException(); + } + + public String debugTitle() { + throw new NotYetImplementedException(); + } + + // /////////////////////////////////////////////////////////////////////// + // non-API + // /////////////////////////////////////////////////////////////////////// + + public JdoNamedQuery getNamedQuery(String queryName) { + return applicationComponents.getNamedQuery(queryName); + } + + /** + * For testing purposes, to allow fixtures to use JDO to initialize the + * database without triggering the objectstore. + * + * @see #resumeListener() + */ + public void suspendListener() { + applicationComponents.suspendListener(); + } + + /** + * For testing purposes, to allow fixtures to use JDO to initialize the + * database without triggering the objectstore. + * + * @see #suspendListener() + */ + public void resumeListener() { + applicationComponents.resumeListener(); + } + + + // /////////////////////////////////////////////////////////////////////// + // Helpers + // /////////////////////////////////////////////////////////////////////// + + private Class clsOf(final TypedOid oid) { + final ObjectSpecification objectSpec = getSpecificationLoader().lookupBySpecId(oid.getObjectSpecId()); + return objectSpec.getCorrespondingClass(); + } + + private Object idValueOf(final RootOid oid) { + final ObjectSpecification objectSpec = getSpecificationLoader().lookupBySpecId(oid.getObjectSpecId()); + return JdoPropertyUtils.idValueOf(oid, objectSpec); + } + + + /** + * Intended for internal and test use only. + */ + public PersistenceManager getPersistenceManager() { + return persistenceManager; + } + + // /////////////////////////////////////////////////////////////////////// + // Dependencies (from constructor) + // /////////////////////////////////////////////////////////////////////// + + public IsisConfiguration getConfiguration() { + return configuration; + } + + public ObjectAdapterFactory getAdapterFactory() { + return adapterFactory; + } + + /** + * @see #setAdapterManager(AdapterManager) + */ + public AdapterManager getAdapterManager() { + return adapterManager; + } + + // /////////////////////////////////////////////////////////////////////// + // Dependencies (injected) + // /////////////////////////////////////////////////////////////////////// + + private void ensureDependenciesInjected() { + ensureThatState(specificationLoader, is(notNullValue())); + ensureThatState(adapterManager, is(notNullValue())); + ensureThatState(hydrator, is(notNullValue())); + //TODO: ensureThatState(hibernateApplicationComponents, is(notNullValue())); + ensureThatState(transactionManager, is(notNullValue())); + } + + /** + * @see #setSpecificationLoader(SpecificationLoader) + */ + public SpecificationLoader getSpecificationLoader() { + return specificationLoader; + } + + /** + * Injected prior to {@link #open()}ing. + *

+ * Injected by owning {@link PersistenceSessionObjectStore} (see + * {@link PersistenceSessionObjectStore#open()}) by virtue of fact that this + * implementation is {@link SpecificationLoaderAware aware} of the + * {@link SpecificationLoader}. + */ + public void setSpecificationLoader(final SpecificationLoader specificationLoader) { + this.specificationLoader = specificationLoader; + } + + /** + * @see #setHydrator(PersistenceSessionHydrator) + */ + public PersistenceSessionHydrator getHydrator() { + return hydrator; + } + + /** + * Injected prior to {@link #open()}ing. + *

+ * Injected by owning {@link PersistenceSessionObjectStore} (see + * {@link PersistenceSessionObjectStore#open()}) by virtue of fact that this + * implementation is {@link PersistenceSessionHydratorAware aware} of the + * {@link PersistenceSessionHydrator}. + */ + public void setHydrator(final PersistenceSessionHydrator hydrator) { + this.hydrator = hydrator; + } + + /** + * @see #getTransactionManager() + */ + public IsisTransactionManager getTransactionManager() { + return transactionManager; + } + + /** + * Injected prior to {@link #open()}ing. + *

+ * Injected by owning {@link PersistenceSessionObjectStore} (see + * {@link PersistenceSessionObjectStore#open()}) by virtue of fact that this + * implementation is {@link NakedObjectTransactionManagerAware aware} of the + * {@link NakedObjectTransactionManager}. + */ + public void setTransactionManager(final IsisTransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + + + +// /** +// * @see #setHibernateApplicationComponents(HibernateMetaDataComponents) +// */ +// public HibernateApplicationComponents getHibernateApplicationComponents() { +// return hibernateApplicationComponents; +// } +// +// /** +// * Injected prior to {@link #open()}ing. +// *

+// * Injected by owning {@link JpaPersistenceSession} (see +// * {@link JpaPersistenceSession#doOpen()); hard-coded into implementation. +// */ +// public void setHibernateApplicationComponents(final HibernateApplicationComponents hibernateApplicationComponents) { +// this.hibernateApplicationComponents = hibernateApplicationComponents; +// } + +} Copied: incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusPersistenceMechanismInstaller.java (from r1359418, incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/DataNucleusPersistenceMechanismInstaller.java) URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusPersistenceMechanismInstaller.java?p2=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusPersistenceMechanismInstaller.java&p1=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/DataNucleusPersistenceMechanismInstaller.java&r1=1359418&r2=1360713&rev=1360713&view=diff ============================================================================== --- incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/DataNucleusPersistenceMechanismInstaller.java (original) +++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/DataNucleusPersistenceMechanismInstaller.java Thu Jul 12 15:05:44 2012 @@ -1,84 +1,84 @@ -package org.apache.isis.runtimes.dflt.objectstores.datanucleus; - -import java.util.Map; - -import org.apache.isis.core.commons.components.Installer; -import org.apache.isis.core.commons.config.IsisConfiguration; -import org.apache.isis.core.metamodel.adapter.ObjectAdapterFactory; -import org.apache.isis.core.metamodel.spec.SpecificationLookup; -import org.apache.isis.runtimes.dflt.bytecode.identity.objectfactory.ObjectFactoryBasic; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.spi.DataNucleusIdentifierGenerator; -import org.apache.isis.runtimes.dflt.objectstores.datanucleus.persistence.spi.DataNucleusSimplePersistAlgorithm; -import org.apache.isis.runtimes.dflt.runtime.installerregistry.installerapi.PersistenceMechanismInstallerAbstract; -import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.ObjectStore; -import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.algorithm.PersistAlgorithm; -import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext; -import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManager; -import org.apache.isis.runtimes.dflt.runtime.system.persistence.IdentifierGenerator; -import org.apache.isis.runtimes.dflt.runtime.system.persistence.ObjectFactory; - -/** - * Configuration files are read in the usual fashion (as per {@link Installer#getConfigurationResources()}, ie will consult all of: - *

    - *
  • persistor_datanucleus.properties - *
  • persistor.properties - *
  • isis.properties - *
- * - *

- * With respect to configuration, all properties under isis.persistor.datanucleus.impl prefix are passed thru verbatim to the OpenDataNucleus runtime. - * For example: - * - * - * - *
Isis PropertyDataNucleus Property
isis.persistor.datanucleus.impl.datanucleus.foo.Bardatanucleus.foo.Bar
- * - */ -public class DataNucleusPersistenceMechanismInstaller extends PersistenceMechanismInstallerAbstract { - - public static final String NAME = "datanucleus"; - - private DataNucleusApplicationComponents applicationComponents = null; - - public DataNucleusPersistenceMechanismInstaller() { - super(NAME); - } - - @Override - protected ObjectStore createObjectStore(IsisConfiguration configuration, ObjectAdapterFactory adapterFactory, AdapterManager adapterManager) { - createDataNucleusApplicationComponentsIfRequired(configuration); - return new DataNucleusObjectStore(configuration, adapterFactory, adapterManager, applicationComponents); - } - - private void createDataNucleusApplicationComponentsIfRequired(IsisConfiguration configuration) { - if(applicationComponents != null) { - return; - } - - final IsisConfiguration dataNucleusConfig = configuration.createSubset("isis.persistor.datanucleus.impl"); - final Map props = dataNucleusConfig.asMap(); - - applicationComponents = new DataNucleusApplicationComponents(props, getSpecificationLoader().allSpecifications()); - } - - @Override - protected IdentifierGenerator createIdentifierGenerator(IsisConfiguration configuration) { - return new DataNucleusIdentifierGenerator(); - } - - - @Override - protected PersistAlgorithm createPersistAlgorithm(IsisConfiguration configuration) { - return new DataNucleusSimplePersistAlgorithm(); - } - - @Override - protected ObjectFactory createObjectFactory(IsisConfiguration configuration) { - return new ObjectFactoryBasic(); - } - - protected SpecificationLookup getSpecificationLoader() { - return IsisContext.getSpecificationLoader(); - } - -} +package org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus; + +import java.util.Map; + +import org.apache.isis.core.commons.components.Installer; +import org.apache.isis.core.commons.config.IsisConfiguration; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterFactory; +import org.apache.isis.core.metamodel.spec.SpecificationLookup; +import org.apache.isis.runtimes.dflt.bytecode.identity.objectfactory.ObjectFactoryBasic; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.spi.DataNucleusIdentifierGenerator; +import org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.spi.DataNucleusSimplePersistAlgorithm; +import org.apache.isis.runtimes.dflt.runtime.installerregistry.installerapi.PersistenceMechanismInstallerAbstract; +import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.ObjectStore; +import org.apache.isis.runtimes.dflt.runtime.persistence.objectstore.algorithm.PersistAlgorithm; +import org.apache.isis.runtimes.dflt.runtime.system.context.IsisContext; +import org.apache.isis.runtimes.dflt.runtime.system.persistence.AdapterManager; +import org.apache.isis.runtimes.dflt.runtime.system.persistence.IdentifierGenerator; +import org.apache.isis.runtimes.dflt.runtime.system.persistence.ObjectFactory; + +/** + * Configuration files are read in the usual fashion (as per {@link Installer#getConfigurationResources()}, ie will consult all of: + *

    + *
  • persistor_datanucleus.properties + *
  • persistor.properties + *
  • isis.properties + *
+ * + *

+ * With respect to configuration, all properties under isis.persistor.datanucleus.impl prefix are passed thru verbatim to the OpenDataNucleus runtime. + * For example: + * + * + * + *
Isis PropertyDataNucleus Property
isis.persistor.datanucleus.impl.datanucleus.foo.Bardatanucleus.foo.Bar
+ * + */ +public class DataNucleusPersistenceMechanismInstaller extends PersistenceMechanismInstallerAbstract { + + public static final String NAME = "datanucleus"; + + private DataNucleusApplicationComponents applicationComponents = null; + + public DataNucleusPersistenceMechanismInstaller() { + super(NAME); + } + + @Override + protected ObjectStore createObjectStore(IsisConfiguration configuration, ObjectAdapterFactory adapterFactory, AdapterManager adapterManager) { + createDataNucleusApplicationComponentsIfRequired(configuration); + return new DataNucleusObjectStore(configuration, adapterFactory, adapterManager, applicationComponents); + } + + private void createDataNucleusApplicationComponentsIfRequired(IsisConfiguration configuration) { + if(applicationComponents != null) { + return; + } + + final IsisConfiguration dataNucleusConfig = configuration.createSubset("isis.persistor.datanucleus.impl"); + final Map props = dataNucleusConfig.asMap(); + + applicationComponents = new DataNucleusApplicationComponents(props, getSpecificationLoader().allSpecifications()); + } + + @Override + protected IdentifierGenerator createIdentifierGenerator(IsisConfiguration configuration) { + return new DataNucleusIdentifierGenerator(); + } + + + @Override + protected PersistAlgorithm createPersistAlgorithm(IsisConfiguration configuration) { + return new DataNucleusSimplePersistAlgorithm(); + } + + @Override + protected ObjectFactory createObjectFactory(IsisConfiguration configuration) { + return new ObjectFactoryBasic(); + } + + protected SpecificationLookup getSpecificationLoader() { + return IsisContext.getSpecificationLoader(); + } + +} Copied: incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/JdoRuntimeException.java (from r1359418, incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/JdoRuntimeException.java) URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/JdoRuntimeException.java?p2=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/JdoRuntimeException.java&p1=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/JdoRuntimeException.java&r1=1359418&r2=1360713&rev=1360713&view=diff ============================================================================== --- incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/JdoRuntimeException.java (original) +++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/JdoRuntimeException.java Thu Jul 12 15:05:44 2012 @@ -1,16 +1,16 @@ -package org.apache.isis.runtimes.dflt.objectstores.datanucleus; - -public class JdoRuntimeException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public JdoRuntimeException() { - super(); - } - - public JdoRuntimeException(String arg0) { - super(arg0); - } - - -} +package org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus; + +public class JdoRuntimeException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public JdoRuntimeException() { + super(); + } + + public JdoRuntimeException(String arg0) { + super(arg0); + } + + +} Copied: incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/metamodel/specloader/progmodelfacets/DataNucleusProgrammingModelFacets.java (from r1359418, incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/metamodel/specloader/progmodelfacets/DataNucleusProgrammingModelFacets.java) URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/metamodel/specloader/progmodelfacets/DataNucleusProgrammingModelFacets.java?p2=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/metamodel/specloader/progmodelfacets/DataNucleusProgrammingModelFacets.java&p1=incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/metamodel/specloader/progmodelfacets/DataNucleusProgrammingModelFacets.java&r1=1359418&r2=1360713&rev=1360713&view=diff ============================================================================== --- incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/datanucleus/metamodel/specloader/progmodelfacets/DataNucleusProgrammingModelFacets.java (original) +++ incubator/isis/trunk/framework/runtimes/dflt/objectstores/jdo/jdo-datanucleus/src/main/java/org/apache/isis/runtimes/dflt/objectstores/jdo/datanucleus/metamodel/specloader/progmodelfacets/DataNucleusProgrammingModelFacets.java Thu Jul 12 15:05:44 2012 @@ -1,43 +1,43 @@ -/* - * 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.runtimes.dflt.objectstores.datanucleus.metamodel.specloader.progmodelfacets; - - -import org.apache.isis.core.progmodel.facets.object.ignore.jdo.RemoveJdoEnhancementTypesFacetFactory; -import org.apache.isis.progmodels.dflt.ProgrammingModelFacetsJava5; -import org.apache.isis.runtimes.dflt.objectstores.jdo.metamodel.specloader.progmodelfacets.JdoProgrammingModelFacets; - -/** - * As per the {@link ProgrammingModelFacetsJava5 Java 5 default programming - * model}, but also - * includes support for JPA. - *

- * Intended to be used by the {@link JpaJavaReflectorInstaller}, which - * additionally sets up other required components needed for JPA support. - */ -public class DataNucleusProgrammingModelFacets extends JdoProgrammingModelFacets { - - public DataNucleusProgrammingModelFacets() { - addFactory(RemoveJdoEnhancementTypesFacetFactory.class); - } - -} - - -// Copyright (c) Naked Objects Group Ltd. +/* + * 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.runtimes.dflt.objectstores.jdo.datanucleus.metamodel.specloader.progmodelfacets; + + +import org.apache.isis.core.progmodel.facets.object.ignore.jdo.RemoveJdoEnhancementTypesFacetFactory; +import org.apache.isis.progmodels.dflt.ProgrammingModelFacetsJava5; +import org.apache.isis.runtimes.dflt.objectstores.jdo.metamodel.specloader.progmodelfacets.JdoProgrammingModelFacets; + +/** + * As per the {@link ProgrammingModelFacetsJava5 Java 5 default programming + * model}, but also + * includes support for JPA. + *

+ * Intended to be used by the {@link JpaJavaReflectorInstaller}, which + * additionally sets up other required components needed for JPA support. + */ +public class DataNucleusProgrammingModelFacets extends JdoProgrammingModelFacets { + + public DataNucleusProgrammingModelFacets() { + addFactory(RemoveJdoEnhancementTypesFacetFactory.class); + } + +} + + +// Copyright (c) Naked Objects Group Ltd.