001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.metamodel.adapter.mgr;
021
022import java.util.concurrent.Callable;
023
024import org.apache.isis.applib.annotation.ActionSemantics;
025import org.apache.isis.core.commons.components.Injectable;
026import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
027import org.apache.isis.core.metamodel.adapter.ResolveState;
028import org.apache.isis.core.metamodel.adapter.oid.Oid;
029import org.apache.isis.core.metamodel.adapter.oid.TypedOid;
030import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
031import org.apache.isis.core.metamodel.adapter.version.Version;
032import org.apache.isis.core.metamodel.spec.ObjectSpecification;
033import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
034
035/**
036 * Responsible for managing the {@link ObjectAdapter adapter}s and {@link Oid
037 * identities} for each and every POJO that is being used by the framework.
038 * 
039 * <p>
040 * It provides a consistent set of adapters in memory, providing an
041 * {@link ObjectAdapter adapter} for the POJOs that are in use ensuring that the
042 * same object is not loaded twice into memory.
043 * 
044 * <p>
045 * Each POJO is given an {@link ObjectAdapter adapter} so that the framework can
046 * work with the POJOs even though it does not understand their types. Each POJO
047 * maps to an {@link ObjectAdapter adapter} and these are reused.
048 */
049public interface AdapterManager extends Injectable {
050
051    /**
052     * Gets the {@link ObjectAdapter adapter} for the {@link Oid} if it exists
053     * in the identity map.
054     * 
055     * @param oid
056     *            - must not be <tt>null</tt>
057     * @return adapter, or <tt>null</tt> if doesn't exist.
058     */
059    ObjectAdapter getAdapterFor(Oid oid);
060
061    /**
062     * Gets the {@link ObjectAdapter adapter} for the specified domain object if
063     * it exists in the identity map.
064     * 
065     * <p>
066     * Provided by the <tt>AdapterManager</tt> when used by framework.
067     * 
068     * @param pojo
069     *            - must not be <tt>null</tt>
070     * @return adapter, or <tt>null</tt> if doesn't exist.
071     */
072    ObjectAdapter getAdapterFor(Object pojo);
073
074    
075    public enum ConcurrencyChecking {
076        NO_CHECK,
077        CHECK;
078        
079        public boolean isChecking() {
080            return this == CHECK;
081        }
082        
083        public static ConcurrencyChecking concurrencyCheckingFor(ActionSemantics.Of actionSemantics) {
084            return actionSemantics == ActionSemantics.Of.SAFE
085                    ? ConcurrencyChecking.NO_CHECK
086                    : ConcurrencyChecking.CHECK;
087        }
088
089        /**
090         * Provides a mechanism to temporarily disable concurrency checking.
091         * 
092         * <p>
093         * A {@link ThreadLocal} is used because typically there is JDO/DataNucleus code between the Isis code
094         * that wishes to disable the concurrency checking and the code (an Isis callback) that needs to
095         * check if checking has been disabled.
096         */
097        private static ThreadLocal<ConcurrencyChecking> concurrencyChecking = new ThreadLocal<ConcurrencyChecking>(){
098            protected ConcurrencyChecking initialValue() {
099                return CHECK;
100            };
101        };
102        
103        /**
104         * Whether concurrency checking is currently disabled.
105         */
106        public static boolean isCurrentlyEnabled() {
107            return concurrencyChecking.get().isChecking();
108        }
109
110        /**
111         * Allows a caller to temporarily disable concurrency checking for the current thread.
112         */
113        public static <T> T executeWithConcurrencyCheckingDisabled(final Callable<T> callable) {
114            final ConcurrencyChecking prior = ConcurrencyChecking.concurrencyChecking.get();
115            try {
116                ConcurrencyChecking.concurrencyChecking.set(ConcurrencyChecking.NO_CHECK);
117                return callable.call();
118            } catch(Exception ex) {
119                throw new RuntimeException(ex);
120            } finally {
121                ConcurrencyChecking.concurrencyChecking.set(prior);
122            }
123        }
124        
125        /**
126         * Allows a caller to temporarily disable concurrency checking for the current thread.
127         */
128        public static void executeWithConcurrencyCheckingDisabled(final Runnable runnable) {
129            final ConcurrencyChecking prior = ConcurrencyChecking.concurrencyChecking.get();
130            try {
131                ConcurrencyChecking.concurrencyChecking.set(ConcurrencyChecking.NO_CHECK);
132                runnable.run();
133            } finally {
134                ConcurrencyChecking.concurrencyChecking.set(prior);
135            }
136        }
137
138
139    }
140
141    /**
142     * As per {@link #adapterFor(TypedOid, ConcurrencyChecking)}, with
143     * {@value org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking#NO_CHECK no checking}.
144     *
145     * <p>
146     * This method  will <i>always</i> return an object, possibly indicating it is persistent; so make sure that you
147     * know that the oid does indeed represent an object you know exists.
148     * </p>
149     */
150    ObjectAdapter adapterFor(TypedOid oid);
151    
152
153    /**
154     * Either returns an existing {@link ObjectAdapter adapter} (as per 
155     * {@link #getAdapterFor(Oid)}), otherwise re-creates an adapter with the 
156     * specified (persistent) {@link Oid}.
157     * 
158     * <p>
159     * Typically called when the {@link Oid} is already known, that is, when
160     * resolving an already-persisted object. Is also available for
161     * <tt>Memento</tt> support however, so {@link Oid} could also represent a
162     * {@link Oid#isTransient() transient} object.
163     * 
164     * <p>
165     * The pojo itself is recreated by delegating to a {@link PojoRecreator} implementation.
166     * The default impl just uses the {@link ObjectSpecification#createObject()};
167     * however object stores (eg JDO/DataNucleus) can provide alternative implementations
168     * in order to ensure that the created pojo is attached to a persistence context.
169     * 
170     * <p>
171     * If the {@link ObjectAdapter adapter} is recreated, its
172     * {@link ResolveState} will be set to {@link ResolveState#GHOST}.
173     *
174     * <p>
175     * The {@link ConcurrencyChecking} parameter determines whether concurrency checking is performed.
176     * If it is requested, then a check is made to ensure that the {@link Oid#getVersion() version} 
177     * of the {@link TypedOid oid} of the recreated adapter is the same as that of the provided {@link TypedOid oid}.
178     * If the version differs, then a {@link ConcurrencyException} is thrown.
179     * 
180     * <p>
181     * ALSO, even if a {@link ConcurrencyException}, then the provided {@link TypedOid oid}'s {@link Version version}
182     * will be {@link TypedOid#setVersion(org.apache.isis.core.metamodel.adapter.version.Version) set} to the current 
183     * value.  This allows the client to retry if they wish.
184     * 
185     * @throws {@link ObjectNotFoundException} if the object does not exist.
186     */
187    ObjectAdapter adapterFor(TypedOid oid, ConcurrencyChecking concurrencyChecking);
188
189    /**
190     * Looks up or creates a standalone (value) or root adapter.
191     */
192    ObjectAdapter adapterFor(Object domainObject);
193    
194    /**
195     * Looks up or creates a standalone (value), aggregated or root adapter.
196     */
197    ObjectAdapter adapterFor(Object domainObject, ObjectAdapter parentAdapter);
198
199    /**
200     * Looks up or creates a collection adapter.
201     */
202    public ObjectAdapter adapterFor(final Object pojo, final ObjectAdapter parentAdapter, OneToManyAssociation collection);
203
204}