package er.extensions.concurrency;
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
* cleans up editing context locks at the end of run() just like the behavior
* at the end of a normal R-R loop.
*
* @author mschrag
*/
public abstract class ERXWOLongResponsePage extends WOLongResponsePage {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
private volatile EOObjectStore _parentObjectStore;
private Long _taskEditingContextTimestampLag;
public ERXWOLongResponsePage(WOContext context) {
super(context);
}
public <T extends WOComponent> T pageWithName(Class<T> componentClass) {
return (T) super.pageWithName(componentClass.getName());
}
public <T extends WOComponent> T pageWithName(Class<T> componentClass, WOContext context) {
return (T) WOApplication.application().pageWithName(componentClass.getName(), context);
}
@Override
public void run() {
ERXApplication._startRequest();
try {
super.run();
}
finally {
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;
}
/**
* <strong>You must manually lock and unlock the editing context returned by
* this method.</strong> 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();
}
}