You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2011/02/19 21:36:24 UTC

svn commit: r1072435 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/ main/java/org/apache/cayenne/access/ test/java/org/apache/cayenne/

Author: aadamchik
Date: Sat Feb 19 20:36:24 2011
New Revision: 1072435

URL: http://svn.apache.org/viewvc?rev=1072435&view=rev
Log:
CAY-1536 Expand deserialization context to provide Injector object instead of just a DataChannel

restoring lazy deserialization mechanism for context, only now it is specified in the superclass

Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/BaseContextTest.java

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java?rev=1072435&r1=1072434&r2=1072435&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/BaseContext.java Sat Feb 19 20:36:24 2011
@@ -25,6 +25,9 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.cayenne.cache.QueryCache;
+import org.apache.cayenne.configuration.CayenneRuntime;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Key;
 import org.apache.cayenne.event.EventManager;
 import org.apache.cayenne.exp.ValueInjector;
 import org.apache.cayenne.graph.CompoundDiff;
@@ -112,6 +115,39 @@ public abstract class BaseContext implem
         graphAction = new ObjectContextGraphAction(this);
     }
 
+    /**
+     * Checks whether this context is attached to Cayenne runtime stack and if not,
+     * attempts to attach itself to the runtime using Injector returned from the call to
+     * {@link CayenneRuntime#getThreadInjector()}. If thread Injector is not available and
+     * the context is not attached, throws CayenneRuntimeException.
+     * <p>
+     * This method is called internally by the context before access to transient
+     * variables to allow the context to attach to the stack lazily following
+     * deserialization.
+     * 
+     * @return true if the context successfully attached to the thread runtime, false - if
+     *         it was already attached.
+     * @since 3.1
+     */
+    protected boolean attachIfNeeded() {
+        if (channel != null) {
+            return false;
+        }
+
+        Injector injector = CayenneRuntime.getThreadInjector();
+        if (injector == null) {
+            throw new CayenneRuntimeException("Can't attach to Cayenne runtime. "
+                    + "Null injector returned from CayenneRuntime.getThreadInjector()");
+        }
+
+        setChannel(injector.getInstance(DataChannel.class));
+        setQueryCache(injector.getInstance(Key.get(
+                QueryCache.class,
+                QUERY_CACHE_INJECTION_KEY)));
+
+        return true;
+    }
+
     public abstract void commitChanges();
 
     public abstract void commitChangesToParent();
@@ -121,6 +157,13 @@ public abstract class BaseContext implem
     public DataChannel getChannel() {
         return channel;
     }
+    
+    /**
+     * @since 3.1
+     */
+    public void setChannel(DataChannel channel) {
+        this.channel = channel;
+    }
 
     public abstract EntityResolver getEntityResolver();
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java?rev=1072435&r1=1072434&r2=1072435&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneContext.java Sat Feb 19 20:36:24 2011
@@ -86,6 +86,7 @@ public class CayenneContext extends Base
     /**
      * Sets the context channel, setting up a listener for channel events.
      */
+    @Override
     public void setChannel(DataChannel channel) {
         if (this.channel != channel) {
 

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java?rev=1072435&r1=1072434&r2=1072435&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java Sat Feb 19 20:36:24 2011
@@ -43,10 +43,6 @@ import org.apache.cayenne.Persistent;
 import org.apache.cayenne.QueryResponse;
 import org.apache.cayenne.access.util.IteratedSelectObserver;
 import org.apache.cayenne.cache.NestedQueryCache;
-import org.apache.cayenne.cache.QueryCache;
-import org.apache.cayenne.configuration.CayenneRuntime;
-import org.apache.cayenne.di.Injector;
-import org.apache.cayenne.di.Key;
 import org.apache.cayenne.event.EventManager;
 import org.apache.cayenne.graph.ChildDiffLoader;
 import org.apache.cayenne.graph.CompoundDiff;
@@ -143,6 +139,7 @@ public class DataContext extends BaseCon
     /**
      * @since 1.2
      */
+    @Override
     public void setChannel(DataChannel channel) {
         if (this.channel != channel) {
 
@@ -1134,44 +1131,33 @@ public class DataContext extends BaseCon
         // TODO: most of this should be in the superclass, especially the code connecting
         // super transient ivars
 
-        // 1. read non-transient properties
+        // read non-transient properties
         in.defaultReadObject();
 
-        // 2. Deserialize local snapshots cache
+        // deserialize local snapshots cache
         if (!isUsingSharedSnapshotCache()) {
             DataRowStore cache = (DataRowStore) in.readObject();
             objectStore.setDataRowCache(cache);
         }
 
-        // 3. set parent channel
-        // call a channel setter to ensure EntityResolver is extracted from channel
-        // call it after DataRowCache is deserialized to avoid incorrect DataRowCache lazy
-        // creation
-        Injector injector = CayenneRuntime.getThreadInjector();
-        if (injector != null) {
-            setChannel(injector.getInstance(DataChannel.class));
-            setQueryCache(injector.getInstance(Key.get(
-                    QueryCache.class,
-                    QUERY_CACHE_INJECTION_KEY)));
-        }
-        else {
-            // throw?
-        }
-
-        // CayenneDataObjects have a transient datacontext
+        // CayenneDataObjects have a transient DataContext
         // because at deserialize time the datacontext may need to be different
         // than the one at serialize time (for programmer defined reasons).
-        // So, when a dataobject is resurrected because it's datacontext was
-        // serialized, it will then set the objects datacontext to the correctone
-        // If deserialized "otherwise", it will not have a datacontext (good)
+        // So, when a DataObject is resurrected because it's DataContext was
+        // serialized, it will then set the objects DataContext to the correct one
+        // If deserialized "otherwise", it will not have a DataContext.
 
         synchronized (getObjectStore()) {
-            Iterator it = objectStore.getObjectIterator();
+            Iterator<?> it = objectStore.getObjectIterator();
             while (it.hasNext()) {
                 Persistent object = (Persistent) it.next();
                 object.setObjectContext(this);
             }
         }
+
+        // ... deferring initialization of transient properties of this context till first
+        // access, so that it can attach to Cayenne runtime using appropriate thread
+        // injector.
     }
 
     /**

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/BaseContextTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/BaseContextTest.java?rev=1072435&r1=1072434&r2=1072435&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/BaseContextTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/BaseContextTest.java Sat Feb 19 20:36:24 2011
@@ -18,10 +18,20 @@
  ****************************************************************/
 package org.apache.cayenne;
 
+import static org.mockito.Mockito.mock;
+
 import java.util.Map;
 
 import junit.framework.TestCase;
 
+import org.apache.cayenne.cache.QueryCache;
+import org.apache.cayenne.configuration.CayenneRuntime;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Key;
+import org.apache.cayenne.di.Module;
+
 public class BaseContextTest extends TestCase {
 
     public void testUserPropertiesLazyInit() {
@@ -32,4 +42,58 @@ public class BaseContextTest extends Tes
         assertNotNull(properties);
         assertSame(properties, context.getUserProperties());
     }
+
+    public void testAttachIfNeeded() {
+
+        final DataChannel channel = mock(DataChannel.class);
+        final QueryCache cache = mock(QueryCache.class);
+
+        Module testModule = new Module() {
+
+            public void configure(Binder binder) {
+                binder.bind(DataChannel.class).toInstance(channel);
+                Key<QueryCache> cacheKey = Key.get(
+                        QueryCache.class,
+                        BaseContext.QUERY_CACHE_INJECTION_KEY);
+                binder.bind(cacheKey).toInstance(cache);
+            }
+        };
+
+        Injector injector = DIBootstrap.createInjector(testModule);
+
+        BaseContext context = new MockBaseContext();
+        assertNull(context.channel);
+        assertNull(context.queryCache);
+
+        Injector oldInjector = CayenneRuntime.getThreadInjector();
+        try {
+
+            CayenneRuntime.bindThreadInjector(injector);
+
+            assertTrue(context.attachIfNeeded());
+            assertSame(channel, context.channel);
+            assertSame(cache, context.queryCache);
+
+            assertFalse(context.attachIfNeeded());
+            assertFalse(context.attachIfNeeded());
+        }
+        finally {
+            CayenneRuntime.bindThreadInjector(oldInjector);
+        }
+    }
+
+    public void testAttachIfNeeded_NoStack() {
+
+        BaseContext context = new MockBaseContext();
+        assertNull(context.channel);
+        assertNull(context.queryCache);
+
+        try {
+            context.attachIfNeeded();
+            fail("No thread stack, must have thrown");
+        }
+        catch (CayenneRuntimeException e) {
+            // expected
+        }
+    }
 }