diff --git a/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXLongResponseTask.java b/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXLongResponseTask.java
index 87401cf55d4..de82b81b297 100644
--- a/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXLongResponseTask.java
+++ b/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXLongResponseTask.java
@@ -7,6 +7,9 @@
import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOComponent;
+import com.webobjects.eocontrol.EOEditingContext;
+import com.webobjects.eocontrol.EOObjectStore;
+import com.webobjects.eocontrol.EOObjectStoreCoordinator;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSForwardException;
import com.webobjects.foundation.NSMutableArray;
@@ -86,7 +89,9 @@ public static NSArray tasks() {
}
public abstract class DefaultImplementation implements Runnable, ERXLongResponseTask {
-
+ private volatile EOObjectStore _parentObjectStore;
+ private Long _taskEditingContextTimestampLag;
+
/** logging support */
public Logger log = Logger.getLogger(ERXUtilities.class);
@@ -344,5 +349,92 @@ public WOComponent nextPage() {
*/
public abstract Object performAction();
+ //---------------------- Copied from ERXTask -------------------------------------
+
+ /**
+ * See Effective Java item #71 for explanation of this threadsafe lazy
+ * initialization technique
+ *
+ * @return the parent, usually an {@link EOObjectStoreCoordinator} to
+ * partition the task's EOF intensive work form the rest of the app.
+ */
+ protected final EOObjectStore parentObjectStore() {
+ EOObjectStore osc = _parentObjectStore;
+ if (osc == null) {
+ synchronized (this) {
+ osc = _parentObjectStore;
+ if (osc == null) {
+ _parentObjectStore = osc = ERXTaskObjectStoreCoordinatorPool.objectStoreCoordinator();
+ }
+ }
+ }
+ return osc;
+ }
+
+ /**
+ * @param parentObjectStore
+ * the parent, usually an {@link EOObjectStoreCoordinator} to
+ * partition the task's EOF intensive work from the rest of the
+ * app. If you are going to manually set this, you should do it
+ * before starting the task.
+ */
+ public final synchronized void setParentObjectStore(EOObjectStore parentObjectStore) {
+ _parentObjectStore = parentObjectStore;
+ }
+
+ /**
+ * You must manually lock and unlock the editing context returned by
+ * this method. It is not recommended that you depend on auto
+ * locking in background threads.
+ *
+ * Even though this method currently returns an auto-locking EC if
+ * auto-locking is turned on for the app, a future update is planned that
+ * will return a manually locking EC from this method even if auto-locking
+ * is turned on for regular ECs used in normal request-response execution.
+ *
+ * @return a new EOEditingContext.
+ */
+ protected EOEditingContext newEditingContext() {
+ EOEditingContext ec = ERXEC.newEditingContext(parentObjectStore());
+ // if this is not a nested EC, we can set the fetch time stamp
+ if (!(parentObjectStore() instanceof EOEditingContext)) {
+ ec.setFetchTimestamp(taskEditingContextTimestampLag());
+ }
+ return ec;
+ }
+
+ /**
+ * By design EOEditingContext's have a fetch timestamp (default is 1 hour)
+ * that effectively creates an in-memory caching system for EOs. This works
+ * great for users browsing through pages in the app. However, experience
+ * has shown that background EOF tasks are performing updates based on the
+ * state of other EOs, and thus we want to This is a long-running task. The
+ * last thing I want to do is perform a long running task with stale EOs, so
+ * we lazily create a fetch timestamp of the current time when we create the
+ * first EC and thus ensure fresh data. Secondly, we continue, by default to
+ * use this timestamp for the duration of the task since experience has
+ * shown that by doing so we can prevent unnecessary database fetching
+ * especially when our task is adding lots of items to a single relationship
+ * in batches.
+ *
+ * However if you want fresh data each time you create an EC in your task,
+ * feel free to set the fetch time stamp to the current time in your task
+ * each time you create a new EC.
+ *
+ * For R-R ec's we prefer fresh data on new pages. However for long running
+ * tasks, it is often best pick a single point in time, usually when the
+ * first ec is created as the timestamp lag. This works well when we are
+ * iterating and making new ec's especially if we are adding 100's of items
+ * to a relationship and cycling ec's
+ *
+ * @return the timestamp lag to use for new ec's created in the task thread.
+ */
+ protected long taskEditingContextTimestampLag() {
+ if (_taskEditingContextTimestampLag == null) {
+ _taskEditingContextTimestampLag = Long.valueOf(System.currentTimeMillis());
+ }
+ return _taskEditingContextTimestampLag.longValue();
+ }
+
}
}
diff --git a/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXTask.java b/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXTask.java
index c4dd1ec983a..1d9a81e8a9c 100644
--- a/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXTask.java
+++ b/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXTask.java
@@ -150,7 +150,7 @@ protected EOEditingContext newEditingContext() {
*
* @return the timestamp lag to use for new ec's created in the task thread.
*/
- private long taskEditingContextTimestampLag() {
+ protected long taskEditingContextTimestampLag() {
if (_taskEditingContextTimestampLag == null) {
_taskEditingContextTimestampLag = Long.valueOf(System.currentTimeMillis());
}
diff --git a/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXWOLongResponsePage.java b/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXWOLongResponsePage.java
index f61159424eb..1b4722857d3 100644
--- a/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXWOLongResponsePage.java
+++ b/Frameworks/Core/ERExtensions/Sources/er/extensions/concurrency/ERXWOLongResponsePage.java
@@ -3,9 +3,13 @@
import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
+import com.webobjects.eocontrol.EOEditingContext;
+import com.webobjects.eocontrol.EOObjectStore;
+import com.webobjects.eocontrol.EOObjectStoreCoordinator;
import com.webobjects.woextensions.WOLongResponsePage;
import er.extensions.appserver.ERXApplication;
+import er.extensions.eof.ERXEC;
/**
* ERXWOLongResponsePage is just like WOLongResponsePage except that it
@@ -21,6 +25,9 @@ public abstract class ERXWOLongResponsePage extends WOLongResponsePage {
* Java Object Serialization Spec
*/
private static final long serialVersionUID = 1L;
+ private volatile EOObjectStore _parentObjectStore;
+ private Long _taskEditingContextTimestampLag;
+
public ERXWOLongResponsePage(WOContext context) {
super(context);
@@ -44,4 +51,92 @@ public void run() {
ERXApplication._endRequest();
}
}
+
+ //---------------------- Copied from ERXTask -------------------------------------
+
+ /**
+ * See Effective Java item #71 for explanation of this threadsafe lazy
+ * initialization technique
+ *
+ * @return the parent, usually an {@link EOObjectStoreCoordinator} to
+ * partition the task's EOF intensive work form the rest of the app.
+ */
+ protected final EOObjectStore parentObjectStore() {
+ EOObjectStore osc = _parentObjectStore;
+ if (osc == null) {
+ synchronized (this) {
+ osc = _parentObjectStore;
+ if (osc == null) {
+ _parentObjectStore = osc = ERXTaskObjectStoreCoordinatorPool.objectStoreCoordinator();
+ }
+ }
+ }
+ return osc;
+ }
+
+ /**
+ * @param parentObjectStore
+ * the parent, usually an {@link EOObjectStoreCoordinator} to
+ * partition the task's EOF intensive work from the rest of the
+ * app. If you are going to manually set this, you should do it
+ * before starting the task.
+ */
+ public final synchronized void setParentObjectStore(EOObjectStore parentObjectStore) {
+ _parentObjectStore = parentObjectStore;
+ }
+
+ /**
+ * You must manually lock and unlock the editing context returned by
+ * this method. It is not recommended that you depend on auto
+ * locking in background threads.
+ *
+ * Even though this method currently returns an auto-locking EC if
+ * auto-locking is turned on for the app, a future update is planned that
+ * will return a manually locking EC from this method even if auto-locking
+ * is turned on for regular ECs used in normal request-response execution.
+ *
+ * @return a new EOEditingContext.
+ */
+ protected EOEditingContext newEditingContext() {
+ EOEditingContext ec = ERXEC.newEditingContext(parentObjectStore());
+ // if this is not a nested EC, we can set the fetch time stamp
+ if (!(parentObjectStore() instanceof EOEditingContext)) {
+ ec.setFetchTimestamp(taskEditingContextTimestampLag());
+ }
+ return ec;
+ }
+
+ /**
+ * By design EOEditingContext's have a fetch timestamp (default is 1 hour)
+ * that effectively creates an in-memory caching system for EOs. This works
+ * great for users browsing through pages in the app. However, experience
+ * has shown that background EOF tasks are performing updates based on the
+ * state of other EOs, and thus we want to This is a long-running task. The
+ * last thing I want to do is perform a long running task with stale EOs, so
+ * we lazily create a fetch timestamp of the current time when we create the
+ * first EC and thus ensure fresh data. Secondly, we continue, by default to
+ * use this timestamp for the duration of the task since experience has
+ * shown that by doing so we can prevent unnecessary database fetching
+ * especially when our task is adding lots of items to a single relationship
+ * in batches.
+ *
+ * However if you want fresh data each time you create an EC in your task,
+ * feel free to set the fetch time stamp to the current time in your task
+ * each time you create a new EC.
+ *
+ * For R-R ec's we prefer fresh data on new pages. However for long running
+ * tasks, it is often best pick a single point in time, usually when the
+ * first ec is created as the timestamp lag. This works well when we are
+ * iterating and making new ec's especially if we are adding 100's of items
+ * to a relationship and cycling ec's
+ *
+ * @return the timestamp lag to use for new ec's created in the task thread.
+ */
+ protected long taskEditingContextTimestampLag() {
+ if (_taskEditingContextTimestampLag == null) {
+ _taskEditingContextTimestampLag = Long.valueOf(System.currentTimeMillis());
+ }
+ return _taskEditingContextTimestampLag.longValue();
+ }
+
}