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(); } }