diff --git a/ccm-cms/src/com/arsdigita/cms/CMSResources.properties b/ccm-cms/src/com/arsdigita/cms/CMSResources.properties index 8d29ac6c5..c4fcdd924 100755 --- a/ccm-cms/src/com/arsdigita/cms/CMSResources.properties +++ b/ccm-cms/src/com/arsdigita/cms/CMSResources.properties @@ -1089,3 +1089,4 @@ cms.ui.item.lifecycle.do=Execute cms.ui.item.lifecycle.do.not_authorized=Your not authorized to publish this item. cms.ui.item.lifecycle.publish_locked=This content item is being (re-)published cms.ui.item.lifecycle.publish_locked.update=Update +cms.ui.lifecycle.publish.error=An error occured while publishing this item. Please inform your system administrator. This item will stay locked until the locked is removed by the system administrator manually. diff --git a/ccm-cms/src/com/arsdigita/cms/CMSResources_de.properties b/ccm-cms/src/com/arsdigita/cms/CMSResources_de.properties index f798bc6e5..fff579d98 100755 --- a/ccm-cms/src/com/arsdigita/cms/CMSResources_de.properties +++ b/ccm-cms/src/com/arsdigita/cms/CMSResources_de.properties @@ -1080,3 +1080,4 @@ cms.ui.item.lifecycle.do=Ausf\u00fchren cms.ui.item.lifecycle.do.not_authorized=Sie sind nicht berechtigt, dieses Item zu publizieren. cms.ui.item.lifecycle.publish_locked=Dieses Content-Item wird gerade (re-)publiziert cms.ui.item.lifecycle.publish_locked.update=Aktualisieren +cms.ui.lifecycle.publish.error=W\u00e4hrend des Publizierens ist ein Fehler aufgetreten. Bitte informieren Sie Ihren System-Administrator. Dieses Item bleibt besperrt, bis der Administrator die Sperre manuell entfernt. diff --git a/ccm-cms/src/com/arsdigita/cms/CMSResources_en_GB.properties b/ccm-cms/src/com/arsdigita/cms/CMSResources_en_GB.properties index 1d4c86b18..e8420a164 100755 --- a/ccm-cms/src/com/arsdigita/cms/CMSResources_en_GB.properties +++ b/ccm-cms/src/com/arsdigita/cms/CMSResources_en_GB.properties @@ -28,3 +28,4 @@ cms.ui.item.lifecycle.do= cms.ui.item.lifecycle.do.not_authorized= cms.ui.item.lifecycle.publish_locked= cms.ui.item.lifecycle.publish_locked.update= +cms.ui.lifecycle.publish.error= diff --git a/ccm-cms/src/com/arsdigita/cms/CMSResources_fr.properties b/ccm-cms/src/com/arsdigita/cms/CMSResources_fr.properties index 5c50fd261..7d415d93e 100755 --- a/ccm-cms/src/com/arsdigita/cms/CMSResources_fr.properties +++ b/ccm-cms/src/com/arsdigita/cms/CMSResources_fr.properties @@ -559,3 +559,4 @@ cms.ui.item.lifecycle.do= cms.ui.item.lifecycle.do.not_authorized= cms.ui.item.lifecycle.publish_locked= cms.ui.item.lifecycle.publish_locked.update= +cms.ui.lifecycle.publish.error= diff --git a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java index b4400a192..7d43980b4 100755 --- a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java +++ b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleAdminPane.java @@ -53,6 +53,7 @@ public class ItemLifecycleAdminPane extends BaseItemPane { private final LayoutPanel m_detailPane; private final LayoutPanel m_selectPane; private final LayoutPanel m_lockedPane; + private final LayoutPanel m_errorPane; public ItemLifecycleAdminPane(final ContentItemRequestLocal item) { m_item = item; @@ -100,6 +101,12 @@ public class ItemLifecycleAdminPane extends BaseItemPane { }); m_lockedPane.setBottom(lockedUpdateLink); + m_errorPane = new LayoutPanel(); + add(m_errorPane); + + final Label errorMsg = new Label(gz("cms.ui.lifecycle.publish.error")); + m_errorPane.setBody(errorMsg); + connect(selectForm, m_detailPane); } @@ -131,7 +138,12 @@ public class ItemLifecycleAdminPane extends BaseItemPane { if (CMSConfig.getInstance().getThreadedPublishing() && PublishLock.getInstance().isLocked(m_item.getContentItem( state))) { - push(state, m_lockedPane); + if (PublishLock.getInstance().hasError(m_item.getContentItem( + state))) { + push(state, m_errorPane); + } else { + push(state, m_lockedPane); + } } else { if (state.isVisibleOnPage(ItemLifecycleAdminPane.this)) { if (m_lifecycle.getLifecycle(state) == null) { diff --git a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleItemPane.java b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleItemPane.java index adf8365b2..85f139e4f 100755 --- a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleItemPane.java +++ b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleItemPane.java @@ -65,10 +65,10 @@ import com.arsdigita.workflow.simple.Workflow; import com.arsdigita.xml.Element; /** - * This class contains the component which displays the information - * for a particular lifecycle, with the ability to edit and delete. - * This information also includes the associated phases for this - * lifecycle, also with the ability to add, edit, and delete. + * This class contains the component which displays the information for a + * particular lifecycle, with the ability to edit and delete. This information + * also includes the associated phases for this lifecycle, also with the ability + * to add, edit, and delete. * * @author Michael Pih * @author Jack Chung @@ -246,6 +246,18 @@ class ItemLifecycleItemPane extends BaseItemPane { if (CMSConfig.getInstance().getThreadedPublishing()) { final Republisher republisher = new Republisher(item); final Thread thread = new Thread(republisher); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + + public void uncaughtException(final Thread thread, + final Throwable ex) { + PublishLock.getInstance().setError(item); + s_log.error(String.format( + "An error occurred while " + + "publishing the item '%s': ", + item.getOID().toString()), + ex); + } + }); thread.start(); @@ -275,11 +287,10 @@ class ItemLifecycleItemPane extends BaseItemPane { /** * Saves OID of item as a string. This is necessary because it is - * not possible to access to same data object instance from - * multiple threads. So we have to create a new instance a the - * data object in the run method. To avoid any sort a problems, - * we store the OID as a string and convert it back to an OID in - * the run method. + * not possible to access to same data object instance from multiple + * threads. So we have to create a new instance a the data object in + * the run method. To avoid any sort a problems, we store the OID as + * a string and convert it back to an OID in the run method. */ private final String itemOid; @@ -316,12 +327,24 @@ class ItemLifecycleItemPane extends BaseItemPane { final ContentItem item = m_item.getContentItem(state); /** - * jensp 2011-12-14: Execute is a thread if - * threaded publishing is active. + * jensp 2011-12-14: Execute is a thread if threaded publishing + * is active. */ if (CMSConfig.getInstance().getThreadedPublishing()) { final Republisher republisher = new Republisher(item); final Thread thread = new Thread(republisher); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + + public void uncaughtException(final Thread thread, + final Throwable ex) { + PublishLock.getInstance().setError(item); + s_log.error(String.format( + "An error occurred while " + + "publishing the item '%s': ", + item.getOID().toString()), + ex); + } + }); thread.start(); @@ -351,11 +374,10 @@ class ItemLifecycleItemPane extends BaseItemPane { /** * Saves OID of item as a string. This is necessary because it is - * not possible to access to same data object instance from - * multiple threads. So we have to create a new instance a the - * data object in the run method. To avoid any sort a problems, - * we store the OID as a string and convert it back to an OID in - * the run method. + * not possible to access to same data object instance from multiple + * threads. So we have to create a new instance a the data object in + * the run method. To avoid any sort a problems, we store the OID as + * a string and convert it back to an OID in the run method. */ private final String itemOid; @@ -399,9 +421,9 @@ class ItemLifecycleItemPane extends BaseItemPane { } /** - * New style pane. Uses a select box for the action to avoid wrong clicks - * on unpublish. - * + * New style pane. Uses a select box for the action to avoid wrong clicks on + * unpublish. + * * @author Jens Pelzetter */ private class ActionForm @@ -474,13 +496,25 @@ class ItemLifecycleItemPane extends BaseItemPane { final ContentItem item = m_item.getContentItem(state); /** - * Republish/Republish and Reset are executed in the thread - * if threaded publishing is active. + * Republish/Republish and Reset are executed in the thread if + * threaded publishing is active. */ if (REPUBLISH.equals(selected)) { if (CMSConfig.getInstance().getThreadedPublishing()) { final RepublishRunner runner = new RepublishRunner(item); final Thread thread = new Thread(runner); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + + public void uncaughtException(final Thread thread, + final Throwable ex) { + PublishLock.getInstance().setError(item); + s_log.error(String.format( + "An error occurred while " + + "publishing the item '%s': ", + item.getOID().toString()), + ex); + } + }); thread.start(); @@ -504,6 +538,18 @@ class ItemLifecycleItemPane extends BaseItemPane { new RepublishAndResetRunner( item); final Thread thread = new Thread(runner); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + + public void uncaughtException(final Thread thread, + final Throwable ex) { + PublishLock.getInstance().setError(item); + s_log.error(String.format( + "An error occurred while " + + "publishing the item '%s': ", + item.getOID().toString()), + ex); + } + }); thread.start(); @@ -532,11 +578,10 @@ class ItemLifecycleItemPane extends BaseItemPane { /** * Saves OID of item as a string. This is necessary because it is - * not possible to access to same data object instance from - * multiple threads. So we have to create a new instance a the - * data object in the run method. To avoid any sort a problems, - * we store the OID as a string and convert it back to an OID in - * the run method. + * not possible to access to same data object instance from multiple + * threads. So we have to create a new instance a the data object in + * the run method. To avoid any sort a problems, we store the OID as + * a string and convert it back to an OID in the run method. */ private final String itemOid; @@ -563,11 +608,10 @@ class ItemLifecycleItemPane extends BaseItemPane { /** * Saves OID of item as a string. This is necessary because it is - * not possible to access to same data object instance from - * multiple threads. So we have to create a new instance a the - * data object in the run method. To avoid any sort a problems, - * we store the OID as a string and convert it back to an OID in - * the run method. + * not possible to access to same data object instance from multiple + * threads. So we have to create a new instance a the data object in + * the run method. To avoid any sort a problems, we store the OID as + * a string and convert it back to an OID in the run method. */ private final String itemOid; diff --git a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleSelectForm.java b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleSelectForm.java index 511a4d26a..0e9ee7c26 100755 --- a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleSelectForm.java +++ b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/ItemLifecycleSelectForm.java @@ -89,7 +89,8 @@ import com.arsdigita.workflow.simple.WorkflowTemplate; * @author Xixi D'moon <xdmoon@redhat.com> * @author Justin Ross <jross@redhat.com> * @author Jens Pelzetter jens@jp-digital.de - * @version $Id: ItemLifecycleSelectForm.java 1643 2007-09-17 14:19:06Z chrisg23 $ + * @version $Id: ItemLifecycleSelectForm.java 1643 2007-09-17 14:19:06Z chrisg23 + * $ */ class ItemLifecycleSelectForm extends BaseForm { @@ -365,12 +366,12 @@ class ItemLifecycleSelectForm extends BaseForm { } /** - * jensp 2011-12-14: Some larger changes to the behavior of the - * process listener. The real action has been moved to the - * @link{Publisher} class. If threaded publishing is active, the publish - * process runs in a separate thread (the item is locked before using - * {@link PublishLock}. If threaded publishing is not active, nothing - * has changed. + * jensp 2011-12-14: Some larger changes to the behavior of the process + * listener. The real action has been moved to the + * @link{Publisher} class. If threaded publishing is active, the publish + * process runs in a separate thread (the item is locked before using + * {@link PublishLock}. If threaded publishing is not active, nothing has + * changed. */ private class ProcessListener implements FormProcessListener { @@ -390,6 +391,18 @@ class ItemLifecycleSelectForm extends BaseForm { } }; final Thread thread = new Thread(threadAction); + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + + public void uncaughtException(final Thread thread, + final Throwable ex) { + PublishLock.getInstance().setError(item); + s_log.error(String.format( + "An error occurred while " + + "publishing the item '%s': ", + item.getOID().toString()), + ex); + } + }); thread.start(); } else { publisher.publish(); @@ -410,171 +423,129 @@ class ItemLifecycleSelectForm extends BaseForm { } } - /*final Integer startHour = (Integer) m_startHour.getValue(state); - Integer startMinute = (Integer) m_startMinute.getValue(state); - - if (startMinute == null) { - startMinute = new Integer(0); + /* + * final Integer startHour = (Integer) m_startHour.getValue(state); + * Integer startMinute = (Integer) m_startMinute.getValue(state); + * + * if (startMinute == null) { startMinute = new Integer(0); } + * + * final Integer startAmpm = (Integer) m_startAmpm.getValue(state); + * + * final Integer endHour = (Integer) m_endHour.getValue(state); + * Integer endMinute = (Integer) m_endMinute.getValue(state); + * + * if (endMinute == null) { endMinute = new Integer(0); } + * + * final Integer endAmpm = (Integer) m_endAmpm.getValue(state); + * + * // Instantiate the instance of the content type. final + * ContentItem item = m_item.getContentItem(state); + * + * final BigDecimal defID = (BigDecimal) + * m_cycleSelect.getValue(state); Assert.exists(defID); final + * LifecycleDefinition cycleDef = new LifecycleDefinition(defID); + * + * java.util.Date startDate = (java.util.Date) + * m_startDate.getValue(state); + * + * final Calendar start = Calendar.getInstance(); + * start.setTime(startDate); start.set(Calendar.AM_PM, + * startAmpm.intValue()); start.set(Calendar.MINUTE, + * startMinute.intValue()); start.set(Calendar.AM_PM, + * startAmpm.intValue()); if (startHour.intValue() != 12) { + * start.set(Calendar.HOUR_OF_DAY, 12 * startAmpm.intValue() + + * startHour.intValue()); start.set(Calendar.HOUR, + * startHour.intValue()); } else { if (startAmpm.intValue() == 0) { + * start.set(Calendar.HOUR_OF_DAY, 0); start.set(Calendar.HOUR, 0); + * } else { start.set(Calendar.HOUR_OF_DAY, 12); + * start.set(Calendar.HOUR, 0); } } startDate = start.getTime(); + * + * java.util.Date endDate = (java.util.Date) + * m_endDate.getValue(state); + * + * if (endDate != null) { final Calendar end = + * Calendar.getInstance(); + * + * end.setTime(endDate); end.set(Calendar.AM_PM, + * endAmpm.intValue()); end.set(Calendar.MINUTE, + * endMinute.intValue()); end.set(Calendar.AM_PM, + * endAmpm.intValue()); + * + * if (endHour.intValue() != 12) { end.set(Calendar.HOUR_OF_DAY, 12 + * * endAmpm.intValue() + endHour.intValue()); + * end.set(Calendar.HOUR, endHour.intValue()); } else { if + * (endAmpm.intValue() == 0) { end.set(Calendar.HOUR_OF_DAY, 0); + * end.set(Calendar.HOUR, 0); } else { end.set(Calendar.HOUR_OF_DAY, + * 12); end.set(Calendar.HOUR, 0); } } endDate = end.getTime(); } + * + * // If the item is already published, remove the current + * lifecycle. // Do not touch the live version. if + * (item.isPublished()) { item.removeLifecycle(item); item.save(); } + * + * // Apply the new lifecycle. ContentItem pending = + * item.publish(cycleDef, startDate); final Lifecycle lifecycle = + * pending.getLifecycle(); + * + * // XXX domlay Whoa. This must be broken for multiphase // + * lifecycles. + * + * if (endDate != null) { + * + * // update individual phases final PhaseCollection phases = + * lifecycle.getPhases(); + * + * while (phases.next()) { final Phase phase = phases.getPhase(); + * java.util.Date thisEnd = phase.getEndDate(); java.util.Date + * thisStart = phase.getStartDate(); if + * (thisStart.compareTo(endDate) > 0) { phase.setStartDate(endDate); + * phase.save(); } + * + * if (thisEnd == null || thisEnd.compareTo(endDate) > 0) { + * phase.setEndDate(endDate); phase.save(); } } } + * + * // endOfCycle may be the original date according to lifecycle + * phase definitions, or endDate if that was before // natural end + * of lifecycle java.util.Date endOfCycle = lifecycle.getEndDate(); + * if (endOfCycle != null) { + * + * // if advance notification is requested (!= 0) // add another + * phase at the start of which the user is notified Integer + * notificationDays = (Integer) m_notificationDays.getValue(state); + * Integer notificationHours = (Integer) + * m_notificationHours.getValue(state); java.util.Date + * notificationDate = null; + * + * int notificationPeriod = 0; if (notificationDays != null) { + * notificationPeriod += notificationDays.intValue() * 24; } if + * (notificationHours != null) { notificationPeriod += + * notificationHours.intValue(); } + * + * if (notificationPeriod > 0) { notificationDate = + * computeNotificationDate(endOfCycle, notificationPeriod); + * s_log.debug("adding custom phase"); Phase expirationImminentPhase + * = lifecycle.addCustomPhase("expirationImminent", new + * Long(notificationDate. getTime()), new + * Long(endOfCycle.getTime())); + * expirationImminentPhase.setListenerClassName( + * "com.arsdigita.cms.lifecycle.NotifyLifecycleListener"); + * expirationImminentPhase.save(); } } + * + * // Force the lifecycle scheduler to run to avoid any // scheduler + * delay for items that should be published // immediately. + * pending.getLifecycle().start(); + * + * item.save(); + * + * final Workflow workflow = m_workflow.getWorkflow(state); try { + * finish(workflow, item, Web.getContext().getUser()); } catch + * (TaskException te) { throw new FormProcessException(te); } // + * redirect to /content-center if streamlined creation mode is + * active. if + * (ContentSection.getConfig().getUseStreamlinedCreation()) { throw + * new RedirectSignal(URL.there(state.getRequest(), + * Utilities.getWorkspaceURL()), true); } - - final Integer startAmpm = (Integer) m_startAmpm.getValue(state); - - final Integer endHour = (Integer) m_endHour.getValue(state); - Integer endMinute = (Integer) m_endMinute.getValue(state); - - if (endMinute == null) { - endMinute = new Integer(0); - } - - final Integer endAmpm = (Integer) m_endAmpm.getValue(state); - - // Instantiate the instance of the content type. - final ContentItem item = m_item.getContentItem(state); - - final BigDecimal defID = (BigDecimal) m_cycleSelect.getValue(state); - Assert.exists(defID); - final LifecycleDefinition cycleDef = new LifecycleDefinition(defID); - - java.util.Date startDate = - (java.util.Date) m_startDate.getValue(state); - - final Calendar start = Calendar.getInstance(); - start.setTime(startDate); - start.set(Calendar.AM_PM, startAmpm.intValue()); - start.set(Calendar.MINUTE, startMinute.intValue()); - start.set(Calendar.AM_PM, startAmpm.intValue()); - if (startHour.intValue() != 12) { - start.set(Calendar.HOUR_OF_DAY, - 12 * startAmpm.intValue() + startHour.intValue()); - start.set(Calendar.HOUR, startHour.intValue()); - } else { - if (startAmpm.intValue() == 0) { - start.set(Calendar.HOUR_OF_DAY, 0); - start.set(Calendar.HOUR, 0); - } else { - start.set(Calendar.HOUR_OF_DAY, 12); - start.set(Calendar.HOUR, 0); - } - } - startDate = start.getTime(); - - java.util.Date endDate = - (java.util.Date) m_endDate.getValue(state); - - if (endDate != null) { - final Calendar end = Calendar.getInstance(); - - end.setTime(endDate); - end.set(Calendar.AM_PM, endAmpm.intValue()); - end.set(Calendar.MINUTE, endMinute.intValue()); - end.set(Calendar.AM_PM, endAmpm.intValue()); - - if (endHour.intValue() != 12) { - end.set(Calendar.HOUR_OF_DAY, - 12 * endAmpm.intValue() + endHour.intValue()); - end.set(Calendar.HOUR, endHour.intValue()); - } else { - if (endAmpm.intValue() == 0) { - end.set(Calendar.HOUR_OF_DAY, 0); - end.set(Calendar.HOUR, 0); - } else { - end.set(Calendar.HOUR_OF_DAY, 12); - end.set(Calendar.HOUR, 0); - } - } - endDate = end.getTime(); - } - - // If the item is already published, remove the current lifecycle. - // Do not touch the live version. - if (item.isPublished()) { - item.removeLifecycle(item); - item.save(); - } - - // Apply the new lifecycle. - ContentItem pending = item.publish(cycleDef, startDate); - final Lifecycle lifecycle = pending.getLifecycle(); - - // XXX domlay Whoa. This must be broken for multiphase - // lifecycles. - - if (endDate != null) { - - // update individual phases - final PhaseCollection phases = lifecycle.getPhases(); - - while (phases.next()) { - final Phase phase = phases.getPhase(); - java.util.Date thisEnd = phase.getEndDate(); - java.util.Date thisStart = phase.getStartDate(); - if (thisStart.compareTo(endDate) > 0) { - phase.setStartDate(endDate); - phase.save(); - } - - if (thisEnd == null || thisEnd.compareTo(endDate) > 0) { - phase.setEndDate(endDate); - phase.save(); - } - } - } - - // endOfCycle may be the original date according to lifecycle phase definitions, or endDate if that was before - // natural end of lifecycle - java.util.Date endOfCycle = lifecycle.getEndDate(); - if (endOfCycle != null) { - - // if advance notification is requested (!= 0) - // add another phase at the start of which the user is notified - Integer notificationDays = - (Integer) m_notificationDays.getValue(state); - Integer notificationHours = - (Integer) m_notificationHours.getValue(state); - java.util.Date notificationDate = null; - - int notificationPeriod = 0; - if (notificationDays != null) { - notificationPeriod += notificationDays.intValue() * 24; - } - if (notificationHours != null) { - notificationPeriod += notificationHours.intValue(); - } - - if (notificationPeriod > 0) { - notificationDate = - computeNotificationDate(endOfCycle, notificationPeriod); - s_log.debug("adding custom phase"); - Phase expirationImminentPhase = - lifecycle.addCustomPhase("expirationImminent", - new Long(notificationDate. - getTime()), - new Long(endOfCycle.getTime())); - expirationImminentPhase.setListenerClassName( - "com.arsdigita.cms.lifecycle.NotifyLifecycleListener"); - expirationImminentPhase.save(); - } - } - - // Force the lifecycle scheduler to run to avoid any - // scheduler delay for items that should be published - // immediately. - pending.getLifecycle().start(); - - item.save(); - - final Workflow workflow = m_workflow.getWorkflow(state); - try { - finish(workflow, item, Web.getContext().getUser()); - } catch (TaskException te) { - throw new FormProcessException(te); - } - // redirect to /content-center if streamlined creation mode is active. - if (ContentSection.getConfig().getUseStreamlinedCreation()) { - throw new RedirectSignal(URL.there(state.getRequest(), - Utilities.getWorkspaceURL()), - true); - }*/ + */ } } @@ -600,8 +571,8 @@ class ItemLifecycleSelectForm extends BaseForm { /** * The constructor collects all necessary data and stores them. - * - * @param state + * + * @param state */ public Publisher(final PageState state) { startHour = (Integer) m_startHour.getValue(state); @@ -689,8 +660,8 @@ class ItemLifecycleSelectForm extends BaseForm { public void publish() { /** - * We have to create a new instance here since it is not possible - * to access the same data object from multiple threads. + * We have to create a new instance here since it is not possible to + * access the same data object from multiple threads. */ final OID oid = OID.valueOf(oidStr); final ContentItem item = (ContentItem) DomainObjectFactory. @@ -997,12 +968,14 @@ class ItemLifecycleSelectForm extends BaseForm { } /** - * Find out at which date a notification (about an item that is about - * to expire) should be sent, based on the endDate (== date at which the - * item is unpublished) and the notification period. - *@param endDate the endDate of the lifecycle, i.e. the date when the item - * is going to be unpublished - *@param notification how many hours the users shouls be notified in advance + * Find out at which date a notification (about an item that is about to + * expire) should be sent, based on the endDate (== date at which the item + * is unpublished) and the notification period. + * + * @param endDate the endDate of the lifecycle, i.e. the date when the item + * is going to be unpublished + * @param notification how many hours the users shouls be notified in + * advance */ private java.util.Date computeNotificationDate(java.util.Date endDate, int notificationPeriod) { diff --git a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/PublishLock.java b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/PublishLock.java index 5944512ba..6ff797a3e 100644 --- a/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/PublishLock.java +++ b/ccm-cms/src/com/arsdigita/cms/ui/lifecycle/PublishLock.java @@ -7,10 +7,10 @@ import com.arsdigita.persistence.SessionManager; import java.util.Calendar; /** - * Used by {@link ItemLifecycleSelectForm} and {@link ItemLifecycleItemPane} - * to lock an item if threaded publishing is active. - * - * @author Jens Pelzetter + * Used by {@link ItemLifecycleSelectForm} and {@link ItemLifecycleItemPane} to + * lock an item if threaded publishing is active. + * + * @author Jens Pelzetter * @version $Id$ */ public class PublishLock { @@ -21,7 +21,8 @@ public class PublishLock { public final static String LOCKED_OID = "lockedOid"; public final static String TIMESTAMP = "timestamp"; public final static String ACTION = "action"; - private static PublishLock instance = new PublishLock(); + public final static String ERROR = "error"; + private static PublishLock instance = new PublishLock(); private PublishLock() { } @@ -33,9 +34,9 @@ public class PublishLock { protected synchronized void lock(final ContentItem item) { lock(item, "publish"); } - + protected synchronized void lock(final ContentItem item, - final String action) { + final String action) { SessionManager.getSession().getTransactionContext().beginTxn(); final DataObject lock = SessionManager.getSession().create( LOCK_OBJECT_TYPE); @@ -47,7 +48,7 @@ public class PublishLock { SessionManager.getSession().getTransactionContext().commitTxn(); } - protected synchronized void unlock(final ContentItem item) { + protected synchronized void unlock(final ContentItem item) { SessionManager.getSession().getTransactionContext().beginTxn(); final DataCollection collection = SessionManager.getSession().retrieve( LOCK_OBJECT_TYPE); @@ -58,7 +59,7 @@ public class PublishLock { final DataObject lock = collection.getDataObject(); lock.delete(); } - collection.close(); + collection.close(); SessionManager.getSession().getTransactionContext().commitTxn(); } @@ -73,6 +74,46 @@ public class PublishLock { } else { collection.close(); return true; - } + } + } + + protected synchronized void setError(final ContentItem item) { + SessionManager.getSession().getTransactionContext().beginTxn(); + final DataCollection collection = SessionManager.getSession().retrieve( + LOCK_OBJECT_TYPE); + collection.addFilter(String.format("%s = '%s'", LOCKED_OID, + item.getOID().toString())); + + if (!collection.isEmpty()) { + collection.next(); + + final DataObject lock = collection.getDataObject(); + lock.set(ACTION, ERROR); + lock.save(); + } + collection.close(); + SessionManager.getSession().getTransactionContext().commitTxn(); + } + + protected synchronized boolean hasError(final ContentItem item) { + final DataCollection collection = SessionManager.getSession().retrieve( + LOCK_OBJECT_TYPE); + collection.addFilter(String.format("%s = '%s'", LOCKED_OID, + item.getOID().toString())); + if (collection.isEmpty()) { + collection.close(); + return false; + } else { + collection.next(); + + final DataObject lock = collection.getDataObject(); + if (ERROR.equals(lock.get(ACTION).toString())) { + collection.close(); + return true; + } else { + collection.close(); + return false; + } + } } }