The publish lock mechanism in ccm-cms now stores the stacktrace of an eventual error in the database. This simplifies the analysis of problems in the publication process. In the catalina.log it is often difficult to find the correct stacktrace.

git-svn-id: https://svn.libreccm.org/ccm/trunk@2546 8810af33-2d31-482b-a856-94f89814c4df
master
jensp 2014-02-28 09:59:07 +00:00
parent 06b2bbb509
commit 4aed32729b
9 changed files with 913 additions and 833 deletions

View File

@ -2,7 +2,7 @@
<ccm:application xmlns:ccm="http://ccm.redhat.com/ccm-project" <ccm:application xmlns:ccm="http://ccm.redhat.com/ccm-project"
name="ccm-cms" name="ccm-cms"
prettyName="Red Hat CCM Content Management System" prettyName="Red Hat CCM Content Management System"
version="6.6.10" version="6.6.11"
release="1" release="1"
webapp="ROOT"> webapp="ROOT">
<ccm:dependencies> <ccm:dependencies>

View File

@ -8,6 +8,7 @@ object type PublishLock {
String[0..1] lockedOid = cms_publish_lock.locked_oid VARCHAR(2048); String[0..1] lockedOid = cms_publish_lock.locked_oid VARCHAR(2048);
Date[0..1] timestamp = cms_publish_lock.lock_timestamp TIMESTAMP; Date[0..1] timestamp = cms_publish_lock.lock_timestamp TIMESTAMP;
String[0..1] action = cms_publish_lock.action VARCHAR(256); String[0..1] action = cms_publish_lock.action VARCHAR(256);
String[0..1] stacktrace = cms_publish_lock.stacktrace CLOB;
object key(id); object key(id);

View File

@ -0,0 +1,23 @@
--
-- Copyright (C) 2014 Jens Pelzetter All Rights Reserved.
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public License
-- as published by the Free Software Foundation; either version 2.1 of
-- the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
--
-- $Id$
-- adds to stacktrace column to the cms_publish_lock table, allowing easier analysis of problems
-- with the publication process
ALTER TABLE cms_publish_lock ADD COLUMN stacktrace TEXT;

View File

@ -0,0 +1,23 @@
--
-- Copyright (C) 2014 Jens Pelzetter All Rights Reserved.
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public License
-- as published by the Free Software Foundation; either version 2.1 of
-- the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
--
-- $Id$
-- Update: Rename com.arsdigta.cms.Workspace to com.arsdigita.cms.ContentCenter
PROMPT Red Hat Enterprise CMS 6.6.10 -> 6.6.11 Upgrade Script (Oracle)
@@ ../default/upgrade/6.6.10-6.6.11/add_cms_publish_lock_stacktrace_column.sql

View File

@ -0,0 +1,28 @@
--
-- Copyright (C) 2014 Jens Pelzetter All Rights Reserved.
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public License
-- as published by the Free Software Foundation; either version 2.1 of
-- the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
--
-- $DateTime$
-- $Id$
-- Update: Rename com.arsdigta.cms.Workspace to com.arsdigita.cms.ContentCenter
\echo Red Hat Enterprise CMS 6.6.10 -> 6.6.11 Upgrade Script (PostgreSQL)
begin;
\i ../default/upgrade/6.6.10-6.6.11/add_cms_publish_lock_stacktrace_column.sql
commit;

View File

@ -77,4 +77,8 @@
<!-- Add an additional field to ContentItem for displaying additional information in the folder browser. --> <!-- Add an additional field to ContentItem for displaying additional information in the folder browser. -->
<script sql="ccm-cms/upgrade/::database::-6.6.9-6.6.10.sql"/> <script sql="ccm-cms/upgrade/::database::-6.6.9-6.6.10.sql"/>
</version> </version>
<version from="6.6.10" to="6.6.11">
<!-- Add stacktrace column for cms_publish_lock -->
<script sql="ccm-cms/upgrade/::database::-6.6.10-6.6.11.sql"/>
</version>
</upgrade> </upgrade>

View File

@ -86,7 +86,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* <p>A form to select and apply a lifecycle to a content item.</p> * <p>
* A form to select and apply a lifecycle to a content item.</p>
* *
* @author Michael Pih * @author Michael Pih
* @author Xixi D'moon &lt;xdmoon@redhat.com&gt; * @author Xixi D'moon &lt;xdmoon@redhat.com&gt;
@ -96,8 +97,7 @@ import org.apache.log4j.Logger;
*/ */
class ItemLifecycleSelectForm extends BaseForm { class ItemLifecycleSelectForm extends BaseForm {
private static final Logger s_log = private static final Logger s_log = Logger.getLogger(ItemLifecycleSelectForm.class);
Logger.getLogger(ItemLifecycleSelectForm.class);
private final static String LIFECYCLE = "lifecycle"; private final static String LIFECYCLE = "lifecycle";
private final static String START_DATE = "start_date"; private final static String START_DATE = "start_date";
private final static String END_DATE = "end_date"; private final static String END_DATE = "end_date";
@ -133,11 +133,9 @@ class ItemLifecycleSelectForm extends BaseForm {
addField(gz("cms.ui.item.lifecycle"), m_cycleSelect); addField(gz("cms.ui.item.lifecycle"), m_cycleSelect);
// Start date // Start date
m_startDate = new Date(new DateParameter(START_DATE) {
m_startDate = @Override
new Date(new DateParameter(START_DATE) {
@Override
protected final Calendar getCalendar(final HttpServletRequest sreq) { protected final Calendar getCalendar(final HttpServletRequest sreq) {
final Calendar cal = super.getCalendar(sreq); final Calendar cal = super.getCalendar(sreq);
@ -145,34 +143,31 @@ class ItemLifecycleSelectForm extends BaseForm {
return cal; return cal;
} }
}); });
addField(gz("cms.ui.item.lifecycle.start_date"), m_startDate); addField(gz("cms.ui.item.lifecycle.start_date"), m_startDate);
// Start time // Start time
final BoxPanel startTime = new BoxPanel(BoxPanel.HORIZONTAL); final BoxPanel startTime = new BoxPanel(BoxPanel.HORIZONTAL);
addField(gz("cms.ui.item.lifecycle.start_time"), startTime); addField(gz("cms.ui.item.lifecycle.start_time"), startTime);
// Hour // Hour
m_startHour = new TextField(new IntegerParameter("start_hour")); m_startHour = new TextField(new IntegerParameter("start_hour"));
startTime.add(m_startHour); startTime.add(m_startHour);
m_startHour.setSize(3); m_startHour.setSize(3);
m_startHour.addValidationListener( m_startHour.addValidationListener(
new NumberInRangeValidationListener(1, 12)); new NumberInRangeValidationListener(1, 12));
// Minute // Minute
m_startMinute = new TextField(new IntegerParameter("start_minute")); m_startMinute = new TextField(new IntegerParameter("start_minute"));
startTime.add(m_startMinute); startTime.add(m_startMinute);
m_startMinute.setSize(3); m_startMinute.setSize(3);
m_startMinute.addValidationListener(new NumberInRangeValidationListener( m_startMinute.addValidationListener(new NumberInRangeValidationListener(
0, 59)); 0, 59));
// AM/PM // AM/PM
m_startAmpm = new SingleSelect(new IntegerParameter("start_ampm")); m_startAmpm = new SingleSelect(new IntegerParameter("start_ampm"));
startTime.add(m_startAmpm); startTime.add(m_startAmpm);
@ -180,15 +175,12 @@ class ItemLifecycleSelectForm extends BaseForm {
m_startAmpm.addOption(new Option("1", "pm")); m_startAmpm.addOption(new Option("1", "pm"));
// Time zone // Time zone
startTime.add(new Label(new TimeZonePrinter())); startTime.add(new Label(new TimeZonePrinter()));
// Expiration date // Expiration date
m_endDate = new Date(new DateParameter(END_DATE) {
m_endDate = @Override
new Date(new DateParameter(END_DATE) {
@Override
protected final Calendar getCalendar(final HttpServletRequest sreq) { protected final Calendar getCalendar(final HttpServletRequest sreq) {
final Calendar cal = super.getCalendar(sreq); final Calendar cal = super.getCalendar(sreq);
@ -196,16 +188,15 @@ class ItemLifecycleSelectForm extends BaseForm {
return cal; return cal;
} }
}); });
addField(gz("cms.ui.item.lifecycle.end_date"), m_endDate); addField(gz("cms.ui.item.lifecycle.end_date"), m_endDate);
// End time // End time
final BoxPanel endTime = new BoxPanel(BoxPanel.HORIZONTAL); final BoxPanel endTime = new BoxPanel(BoxPanel.HORIZONTAL);
addField(gz("cms.ui.item.lifecycle.end_time"), endTime); addField(gz("cms.ui.item.lifecycle.end_time"), endTime);
// Hour // Hour
m_endHour = new TextField(new IntegerParameter("end_hour")); m_endHour = new TextField(new IntegerParameter("end_hour"));
endTime.add(m_endHour); endTime.add(m_endHour);
@ -214,16 +205,14 @@ class ItemLifecycleSelectForm extends BaseForm {
12)); 12));
// Minute // Minute
m_endMinute = new TextField(new IntegerParameter("end_minute")); m_endMinute = new TextField(new IntegerParameter("end_minute"));
endTime.add(m_endMinute); endTime.add(m_endMinute);
m_endMinute.setSize(3); m_endMinute.setSize(3);
m_endMinute.addValidationListener( m_endMinute.addValidationListener(
new NumberInRangeValidationListener(0, 59)); new NumberInRangeValidationListener(0, 59));
// AM/PM // AM/PM
m_endAmpm = new SingleSelect(new IntegerParameter("end_ampm")); m_endAmpm = new SingleSelect(new IntegerParameter("end_ampm"));
endTime.add(m_endAmpm); endTime.add(m_endAmpm);
@ -232,12 +221,9 @@ class ItemLifecycleSelectForm extends BaseForm {
endTime.add(new Label(new TimeZonePrinter())); endTime.add(new Label(new TimeZonePrinter()));
m_notificationDays = new TextField(new IntegerParameter(NOTIFICATION_DAYS));
m_notificationDays =
new TextField(new IntegerParameter(NOTIFICATION_DAYS));
m_notificationDays.setSize(4); m_notificationDays.setSize(4);
m_notificationHours = m_notificationHours = new TextField(new IntegerParameter(NOTIFICATION_HOURS));
new TextField(new IntegerParameter(NOTIFICATION_HOURS));
m_notificationHours.setSize(4); m_notificationHours.setSize(4);
SimpleContainer cont = new SimpleContainer(); SimpleContainer cont = new SimpleContainer();
cont.add(m_notificationDays); cont.add(m_notificationDays);
@ -251,12 +237,9 @@ class ItemLifecycleSelectForm extends BaseForm {
// A hidden field that checks to see if the user wants publish // A hidden field that checks to see if the user wants publish
// with a start time earlier than current time. // with a start time earlier than current time.
addAction(new Submit("finish", gz("cms.ui.item.lifecycle.publish"))); addAction(new Submit("finish", gz("cms.ui.item.lifecycle.publish")));
// Form listeners // Form listeners
addValidationListener(new ValidationListener()); addValidationListener(new ValidationListener());
addSecurityListener(PUBLISH, m_item); addSecurityListener(PUBLISH, m_item);
addInitListener(new InitListener()); addInitListener(new InitListener());
@ -265,13 +248,11 @@ class ItemLifecycleSelectForm extends BaseForm {
private class OptionPrinter implements PrintListener { private class OptionPrinter implements PrintListener {
@Override @Override
public final void prepare(final PrintEvent e) { public final void prepare(final PrintEvent e) {
final ContentSection section = final ContentSection section = CMS.getContext().getContentSection();
CMS.getContext().getContentSection();
final LifecycleDefinitionCollection ldc = final LifecycleDefinitionCollection ldc = section.getLifecycleDefinitions();
section.getLifecycleDefinitions();
ldc.addOrder("label"); ldc.addOrder("label");
final SingleSelect target = (SingleSelect) e.getTarget(); final SingleSelect target = (SingleSelect) e.getTarget();
@ -283,7 +264,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// XXX domlay this seems a little weak. perhaps // XXX domlay this seems a little weak. perhaps
// there's a better way to determine if a lifecycle is // there's a better way to determine if a lifecycle is
// ready to be applied to an item. // ready to be applied to an item.
if (!pdc.isEmpty()) { if (!pdc.isEmpty()) {
target.addOption(new Option(ld.getID().toString(), target.addOption(new Option(ld.getID().toString(),
ld.getLabel())); ld.getLabel()));
@ -294,11 +274,12 @@ class ItemLifecycleSelectForm extends BaseForm {
ldc.close(); ldc.close();
} }
} }
private class InitListener implements FormInitListener { private class InitListener implements FormInitListener {
@Override @Override
public final void init(final FormSectionEvent e) { public final void init(final FormSectionEvent e) {
final PageState state = e.getPageState(); final PageState state = e.getPageState();
@ -308,18 +289,15 @@ class ItemLifecycleSelectForm extends BaseForm {
// If the item is published, select the currently // If the item is published, select the currently
// associated lifecycle. // associated lifecycle.
final LifecycleDefinition ld = final LifecycleDefinition ld = item.getLifecycle().
item.getLifecycle(). getLifecycleDefinition();
getLifecycleDefinition();
m_cycleSelect.setValue(state, ld.getID()); m_cycleSelect.setValue(state, ld.getID());
} else { } else {
// Set the default lifecycle (if it exists). // Set the default lifecycle (if it exists).
final ContentSection section = final ContentSection section = CMS.getContext().getContentSection();
CMS.getContext().getContentSection(); final LifecycleDefinition ld = ContentTypeLifecycleDefinition.
final LifecycleDefinition ld = getLifecycleDefinition(section, item.getContentType());
ContentTypeLifecycleDefinition.
getLifecycleDefinition(section, item.getContentType());
if (ld != null) { if (ld != null) {
m_cycleSelect.setValue(state, ld.getID()); m_cycleSelect.setValue(state, ld.getID());
@ -327,10 +305,9 @@ class ItemLifecycleSelectForm extends BaseForm {
} }
// Set the default start date. // Set the default start date.
// XXX Isn't just new Date() sufficient? // XXX Isn't just new Date() sufficient?
final java.util.Date start = new java.util.Date(System. final java.util.Date start = new java.util.Date(System.
currentTimeMillis()); currentTimeMillis());
m_startDate.setValue(state, start); m_startDate.setValue(state, start);
final Calendar calendar = Calendar.getInstance(); final Calendar calendar = Calendar.getInstance();
@ -339,12 +316,11 @@ class ItemLifecycleSelectForm extends BaseForm {
// If the hour is 12, then Calendar.get(Calendar.HOUR) // If the hour is 12, then Calendar.get(Calendar.HOUR)
// returns 0 (from the 24 hour time - 12). We want it to // returns 0 (from the 24 hour time - 12). We want it to
// return 12. // return 12.
if (calendar.get(Calendar.HOUR) == 0) { if (calendar.get(Calendar.HOUR) == 0) {
m_startHour.setValue(state, new Integer(12)); m_startHour.setValue(state, new Integer(12));
} else { } else {
m_startHour.setValue(state, new Integer(calendar.get( m_startHour.setValue(state, new Integer(calendar.get(
Calendar.HOUR))); Calendar.HOUR)));
} }
final Integer min = new Integer(calendar.get(Calendar.MINUTE)); final Integer min = new Integer(calendar.get(Calendar.MINUTE));
@ -358,22 +334,21 @@ class ItemLifecycleSelectForm extends BaseForm {
m_startAmpm.setValue(state, m_startAmpm.setValue(state,
new Integer(calendar.get(Calendar.AM_PM))); new Integer(calendar.get(Calendar.AM_PM)));
BigInteger[] defaultTime = BigInteger[] defaultTime = BigInteger.valueOf(ContentSection.getConfig().
BigInteger.valueOf(ContentSection.getConfig(). getDefaultNotificationTime()).
getDefaultNotificationTime()). divideAndRemainder(BigInteger.valueOf(24));
divideAndRemainder(BigInteger.valueOf(24));
m_notificationDays.setValue(state, new Integer(defaultTime[0]. m_notificationDays.setValue(state, new Integer(defaultTime[0].
intValue())); intValue()));
m_notificationHours.setValue(state, new Integer(defaultTime[1]. m_notificationHours.setValue(state, new Integer(defaultTime[1].
intValue())); intValue()));
} }
} }
/** /**
* jensp 2011-12-14: Some larger changes to the behavior of the process * jensp 2011-12-14: Some larger changes to the behavior of the process listener. The real
* listener. The real action has been moved to the * action has been moved to the
* @link{Publisher} class. If threaded publishing is active, the publish * @link{Publisher} class. If threaded publishing is active, the publish
* process runs in a separate thread (the item is locked before using * process runs in a separate thread (the item is locked before using
* {@link PublishLock}. If threaded publishing is not active, nothing has * {@link PublishLock}. If threaded publishing is not active, nothing has
@ -381,9 +356,9 @@ class ItemLifecycleSelectForm extends BaseForm {
*/ */
private class ProcessListener implements FormProcessListener { private class ProcessListener implements FormProcessListener {
@Override @Override
public final void process(final FormSectionEvent e) public final void process(final FormSectionEvent e)
throws FormProcessException { throws FormProcessException {
final PageState state = e.getPageState(); final PageState state = e.getPageState();
final ContentItem item = m_item.getContentItem(state); final ContentItem item = m_item.getContentItem(state);
@ -391,24 +366,29 @@ class ItemLifecycleSelectForm extends BaseForm {
if (CMSConfig.getInstance().getThreadedPublishing()) { if (CMSConfig.getInstance().getThreadedPublishing()) {
final Runnable threadAction = new Runnable() { final Runnable threadAction = new Runnable() {
@Override @Override
public void run() { public void run() {
PublishLock.getInstance().lock(item); PublishLock.getInstance().lock(item);
publisher.publish(); publisher.publish();
PublishLock.getInstance().unlock(item); PublishLock.getInstance().unlock(item);
} }
}; };
final Thread thread = new Thread(threadAction); final Thread thread = new Thread(threadAction);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override @Override
public void uncaughtException(final Thread thread, public void uncaughtException(final Thread thread,
final Throwable ex) { final Throwable ex) {
PublishLock.getInstance().setError(item); final StringWriter strWriter = new StringWriter();
final PrintWriter writer = new PrintWriter(strWriter);
ex.printStackTrace(writer);
PublishLock.getInstance().setError(item, strWriter.toString());
s_log.error(String.format( s_log.error(String.format(
"An error occurred while " "An error occurred while "
+ "publishing the item '%s': ", + "publishing the item '%s': ",
item.getOID().toString()), item.getOID().toString()),
ex); ex);
if ((CMSConfig.getInstance().getPublicationFailureSender() if ((CMSConfig.getInstance().getPublicationFailureSender()
@ -419,21 +399,21 @@ class ItemLifecycleSelectForm extends BaseForm {
} }
final PartyCollection receiverParties = Party. final PartyCollection receiverParties = Party.
retrieveAllParties(); retrieveAllParties();
Party receiver = null; Party receiver = null;
receiverParties.addEqualsFilter("primaryEmail", receiverParties.addEqualsFilter("primaryEmail",
CMSConfig.getInstance(). CMSConfig.getInstance().
getPublicationFailureReceiver()); getPublicationFailureReceiver());
if (receiverParties.next()) { if (receiverParties.next()) {
receiver = receiverParties.getParty(); receiver = receiverParties.getParty();
} }
receiverParties.close(); receiverParties.close();
final PartyCollection senderParties = Party. final PartyCollection senderParties = Party.
retrieveAllParties(); retrieveAllParties();
Party sender = null; Party sender = null;
senderParties.addEqualsFilter("primaryEmail", CMSConfig. senderParties.addEqualsFilter("primaryEmail", CMSConfig.
getInstance().getPublicationFailureReceiver()); getInstance().getPublicationFailureReceiver());
if (senderParties.next()) { if (senderParties.next()) {
sender = senderParties.getParty(); sender = senderParties.getParty();
} }
@ -442,23 +422,24 @@ class ItemLifecycleSelectForm extends BaseForm {
if ((sender != null) && (receiver != null)) { if ((sender != null) && (receiver != null)) {
final Writer traceWriter = new StringWriter(); final Writer traceWriter = new StringWriter();
final PrintWriter printWriter = new PrintWriter( final PrintWriter printWriter = new PrintWriter(
traceWriter); traceWriter);
ex.printStackTrace(printWriter); ex.printStackTrace(printWriter);
final Notification notification = new Notification( final Notification notification = new Notification(
sender, sender,
receiver, receiver,
String.format("Failed to publish item '%s'", String.format("Failed to publish item '%s'",
item.getOID().toString()), item.getOID().toString()),
String.format("Publishing item '%s' failed " String.format("Publishing item '%s' failed "
+ "with error message: %s.\n\n" + "with error message: %s.\n\n"
+ "Stacktrace:\n%s", + "Stacktrace:\n%s",
item.getOID().toString(), item.getOID().toString(),
ex.getMessage(), ex.getMessage(),
traceWriter.toString())); traceWriter.toString()));
notification.save(); notification.save();
} }
} }
}); });
thread.start(); thread.start();
} else { } else {
@ -467,16 +448,16 @@ class ItemLifecycleSelectForm extends BaseForm {
if (CMSConfig.getInstance().getThreadedPublishing()) { if (CMSConfig.getInstance().getThreadedPublishing()) {
throw new RedirectSignal( throw new RedirectSignal(
URL.getDispatcherPath() URL.getDispatcherPath()
+ ContentItemPage.getItemURL(item, + ContentItemPage.getItemURL(item,
ContentItemPage.PUBLISHING_TAB), ContentItemPage.PUBLISHING_TAB),
true); true);
} else { } else {
if (ContentSection.getConfig().getUseStreamlinedCreation()) { if (ContentSection.getConfig().getUseStreamlinedCreation()) {
throw new RedirectSignal( throw new RedirectSignal(
URL.there(state.getRequest(), URL.there(state.getRequest(),
ContentCenter.getURL()), ContentCenter.getURL()),
true); true);
} }
} }
@ -603,6 +584,7 @@ class ItemLifecycleSelectForm extends BaseForm {
* Utilities.getWorkspaceURL()), true); } * Utilities.getWorkspaceURL()), true); }
*/ */
} }
} }
/** /**
@ -716,12 +698,12 @@ class ItemLifecycleSelectForm extends BaseForm {
public void publish() { public void publish() {
/** /**
* We have to create a new instance here since it is not possible to * We have to create a new instance here since it is not possible to access the same
* access the same data object from multiple threads. * data object from multiple threads.
*/ */
final OID oid = OID.valueOf(oidStr); final OID oid = OID.valueOf(oidStr);
final ContentItem item = (ContentItem) DomainObjectFactory. final ContentItem item = (ContentItem) DomainObjectFactory.
newInstance(oid); newInstance(oid);
// If the item is already published, remove the current lifecycle. // If the item is already published, remove the current lifecycle.
// Do not touch the live version. // Do not touch the live version.
@ -740,7 +722,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// XXX domlay Whoa. This must be broken for multiphase // XXX domlay Whoa. This must be broken for multiphase
// lifecycles. // lifecycles.
if (endDate != null) { if (endDate != null) {
// update individual phases // update individual phases
@ -780,16 +761,16 @@ class ItemLifecycleSelectForm extends BaseForm {
} }
if (notificationPeriod > 0) { if (notificationPeriod > 0) {
notificationDate = notificationDate = computeNotificationDate(endOfCycle, notificationPeriod);
computeNotificationDate(endOfCycle, notificationPeriod);
s_log.debug("adding custom phase"); s_log.debug("adding custom phase");
Phase expirationImminentPhase = Phase expirationImminentPhase = lifecycle.addCustomPhase("expirationImminent",
lifecycle.addCustomPhase("expirationImminent", new Long(
new Long(notificationDate. notificationDate.
getTime()), getTime()),
new Long(endOfCycle.getTime())); new Long(endOfCycle.
getTime()));
expirationImminentPhase.setListenerClassName( expirationImminentPhase.setListenerClassName(
"com.arsdigita.cms.lifecycle.NotifyLifecycleListener"); "com.arsdigita.cms.lifecycle.NotifyLifecycleListener");
expirationImminentPhase.save(); expirationImminentPhase.save();
} }
} }
@ -802,9 +783,8 @@ class ItemLifecycleSelectForm extends BaseForm {
item.save(); item.save();
if (workflowOid != null) { if (workflowOid != null) {
final Workflow workflow = final Workflow workflow = (Workflow) DomainObjectFactory.newInstance(OID.
(Workflow) DomainObjectFactory.newInstance(OID. valueOf(workflowOid));
valueOf(workflowOid));
try { try {
finish(workflow, item, user); finish(workflow, item, user);
} catch (TaskException ex) { } catch (TaskException ex) {
@ -812,16 +792,17 @@ class ItemLifecycleSelectForm extends BaseForm {
} }
} }
} }
} }
static void finish(Workflow workflow, ContentItem item, User user) throws static void finish(Workflow workflow, ContentItem item, User user) throws
TaskException { TaskException {
if ((workflow != null) && (user != null)) { if ((workflow != null) && (user != null)) {
final Engine engine = Engine.getInstance(CMSEngine.CMS_ENGINE_TYPE); final Engine engine = Engine.getInstance(CMSEngine.CMS_ENGINE_TYPE);
// ; // ;
final Iterator iter = engine.getEnabledTasks(user, workflow.getID()). final Iterator iter = engine.getEnabledTasks(user, workflow.getID()).
iterator(); iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
final CMSTask task = (CMSTask) iter.next(); final CMSTask task = (CMSTask) iter.next();
@ -842,8 +823,8 @@ class ItemLifecycleSelectForm extends BaseForm {
workflow.delete(); workflow.delete();
workflow = t.instantiateNewWorkflow(); workflow = t.instantiateNewWorkflow();
workflow.setObject(item); workflow.setObject(item);
/* Startring the workflow will probably do the wrong thing, because most of the time /* Startring the workflow will probably do the wrong thing, because most of the time
* the current user would be a publisher, not an author */ * the current user would be a publisher, not an author */
// workflow.start(user); // workflow.start(user);
workflow.save(); workflow.save();
} }
@ -852,14 +833,14 @@ class ItemLifecycleSelectForm extends BaseForm {
private class ValidationListener implements FormValidationListener { private class ValidationListener implements FormValidationListener {
@Override @Override
public void validate(FormSectionEvent e) throws FormProcessException { public void validate(FormSectionEvent e) throws FormProcessException {
final PageState state = e.getPageState(); final PageState state = e.getPageState();
final Integer startHour = (Integer) m_startHour.getValue(state); final Integer startHour = (Integer) m_startHour.getValue(state);
if (startHour == null) { if (startHour == null) {
throw new FormProcessException(lz( throw new FormProcessException(lz(
"cms.ui.item.start_time_incomplete")); "cms.ui.item.start_time_incomplete"));
} }
Integer startMinute = (Integer) m_startMinute.getValue(state); Integer startMinute = (Integer) m_startMinute.getValue(state);
@ -870,14 +851,13 @@ class ItemLifecycleSelectForm extends BaseForm {
Integer startAmpm = (Integer) m_startAmpm.getValue(state); Integer startAmpm = (Integer) m_startAmpm.getValue(state);
java.util.Date startDate = (java.util.Date) m_startDate.getValue( java.util.Date startDate = (java.util.Date) m_startDate.getValue(
state); state);
if (startDate == null) { if (startDate == null) {
throw new FormProcessException(lz( throw new FormProcessException(lz(
"cms.ui.item.lifecycle.start_date_invalid")); "cms.ui.item.lifecycle.start_date_invalid"));
} }
java.util.Date nowDate = java.util.Date nowDate = new java.util.Date(System.currentTimeMillis());
new java.util.Date(System.currentTimeMillis());
Calendar cStart = Calendar.getInstance(); Calendar cStart = Calendar.getInstance();
Calendar cNow = Calendar.getInstance(); Calendar cNow = Calendar.getInstance();
@ -900,7 +880,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// Give the user extra 5 minutes before form complains // Give the user extra 5 minutes before form complains
// start time's in the past. // start time's in the past.
cStart.set(Calendar.MINUTE, startMinute.intValue() + 5); cStart.set(Calendar.MINUTE, startMinute.intValue() + 5);
cStart.set(Calendar.AM_PM, startAmpm.intValue()); cStart.set(Calendar.AM_PM, startAmpm.intValue());
cStart.set(Calendar.SECOND, cNow.get(Calendar.SECOND)); cStart.set(Calendar.SECOND, cNow.get(Calendar.SECOND));
@ -908,17 +887,16 @@ class ItemLifecycleSelectForm extends BaseForm {
if (cNow.after(cStart)) { if (cNow.after(cStart)) {
throw new FormProcessException(lz( throw new FormProcessException(lz(
"cms.ui.item.lifecycle.start_date_in_past")); "cms.ui.item.lifecycle.start_date_in_past"));
} }
Integer endHour = (Integer) m_endHour.getValue(state); Integer endHour = (Integer) m_endHour.getValue(state);
Integer endMinute = (Integer) m_endMinute.getValue(state); Integer endMinute = (Integer) m_endMinute.getValue(state);
java.util.Date endDate = java.util.Date endDate = (java.util.Date) m_endDate.getValue(state);
(java.util.Date) m_endDate.getValue(state);
if (endHour == null && (endMinute != null || endDate != null)) { if (endHour == null && (endMinute != null || endDate != null)) {
throw new FormProcessException(lz( throw new FormProcessException(lz(
"cms.ui.item.lifecycle.end_time_incomplete")); "cms.ui.item.lifecycle.end_time_incomplete"));
} }
if (endMinute == null && endHour != null) { if (endMinute == null && endHour != null) {
@ -931,7 +909,7 @@ class ItemLifecycleSelectForm extends BaseForm {
if (endDate == null && !timeBlank) { if (endDate == null && !timeBlank) {
throw new FormProcessException(lz( throw new FormProcessException(lz(
"cms.ui.item.lifecycle.end_date_invalid")); "cms.ui.item.lifecycle.end_date_invalid"));
} }
if (endDate != null) { if (endDate != null) {
@ -954,7 +932,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// Give the user extra 5 minutes before form complains // Give the user extra 5 minutes before form complains
// end time's in the past. // end time's in the past.
cEnd.set(Calendar.MINUTE, endMinute.intValue() + 5); cEnd.set(Calendar.MINUTE, endMinute.intValue() + 5);
cEnd.set(Calendar.AM_PM, endAmpm.intValue()); cEnd.set(Calendar.AM_PM, endAmpm.intValue());
cEnd.set(Calendar.SECOND, cNow.get(Calendar.SECOND)); cEnd.set(Calendar.SECOND, cNow.get(Calendar.SECOND));
@ -963,13 +940,11 @@ class ItemLifecycleSelectForm extends BaseForm {
//check if the end date is prior to the start date //check if the end date is prior to the start date
if (cStart.after(cEnd)) { if (cStart.after(cEnd)) {
throw new FormProcessException(lz( throw new FormProcessException(lz(
"cms.ui.item.lifecycle.end_date_before_start_date")); "cms.ui.item.lifecycle.end_date_before_start_date"));
} }
Integer notificationDays = Integer notificationDays = (Integer) m_notificationDays.getValue(state);
(Integer) m_notificationDays.getValue(state); Integer notificationHours = (Integer) m_notificationHours.getValue(state);
Integer notificationHours =
(Integer) m_notificationHours.getValue(state);
int notificationPeriod = 0; int notificationPeriod = 0;
if (notificationDays != null) { if (notificationDays != null) {
@ -981,18 +956,17 @@ class ItemLifecycleSelectForm extends BaseForm {
if (notificationPeriod > 0) { if (notificationPeriod > 0) {
// point in time for notification == end date - notificationPeriod // point in time for notification == end date - notificationPeriod
java.util.Date notificationDate = java.util.Date notificationDate = computeNotificationDate(cEnd.getTime(),
computeNotificationDate(cEnd.getTime(), notificationPeriod);
notificationPeriod);
s_log.debug("cStart (Date): " + cStart.getTime()); s_log.debug("cStart (Date): " + cStart.getTime());
s_log.debug("notificationDate: " + notificationDate); s_log.debug("notificationDate: " + notificationDate);
// complain if date for notification is before the start date // complain if date for notification is before the start date
if (notificationDate.before(cStart.getTime())) { if (notificationDate.before(cStart.getTime())) {
s_log.debug("notification date is before start date!"); s_log.debug("notification date is before start date!");
String errorMessage = (String) GlobalizationUtil. String errorMessage = (String) GlobalizationUtil.
globalize( globalize(
"cms.ui.item.notification_period_before_start"). "cms.ui.item.notification_period_before_start").
localize(); localize();
throw new FormProcessException(errorMessage); throw new FormProcessException(errorMessage);
} else { } else {
s_log.debug("notification date is after start date, OK"); s_log.debug("notification date is after start date, OK");
@ -1000,11 +974,12 @@ class ItemLifecycleSelectForm extends BaseForm {
} }
} }
} }
} }
public class TimeZonePrinter implements PrintListener { public class TimeZonePrinter implements PrintListener {
@Override @Override
public void prepare(PrintEvent e) { public void prepare(PrintEvent e) {
final Label target = (Label) e.getTarget(); final Label target = (Label) e.getTarget();
if (ContentSection.getConfig().getHideTimezone()) { if (ContentSection.getConfig().getHideTimezone()) {
@ -1018,24 +993,22 @@ class ItemLifecycleSelectForm extends BaseForm {
mStart.setTime((java.util.Date) m_startDate.getValue(state)); mStart.setTime((java.util.Date) m_startDate.getValue(state));
} }
final String zone = final String zone = mStart.getTimeZone().getDisplayName(true,
mStart.getTimeZone().getDisplayName(true, TimeZone.SHORT);
TimeZone.SHORT);
target.setLabel(zone); target.setLabel(zone);
} }
} }
} }
/** /**
* Find out at which date a notification (about an item that is about to * Find out at which date a notification (about an item that is about to expire) should be sent,
* expire) should be sent, based on the endDate (== date at which the item * based on the endDate (== date at which the item is unpublished) and the notification period.
* is unpublished) and the notification period.
* *
* @param endDate the endDate of the lifecycle, i.e. the date when the item * @param endDate the endDate of the lifecycle, i.e. the date when the item is going to be
* is going to be unpublished * unpublished
* @param notification how many hours the users shouls be notified in * @param notification how many hours the users shouls be notified in advance
* advance
*/ */
private java.util.Date computeNotificationDate(java.util.Date endDate, private java.util.Date computeNotificationDate(java.util.Date endDate,
int notificationPeriod) { int notificationPeriod) {
@ -1046,4 +1019,5 @@ class ItemLifecycleSelectForm extends BaseForm {
return new java.util.Date(endDate.getTime() - (long) notificationPeriod return new java.util.Date(endDate.getTime() - (long) notificationPeriod
* 3600000L); * 3600000L);
} }
} }

View File

@ -22,6 +22,7 @@ public class PublishLock {
public final static String TIMESTAMP = "timestamp"; public final static String TIMESTAMP = "timestamp";
public final static String ACTION = "action"; public final static String ACTION = "action";
public final static String ERROR = "error"; public final static String ERROR = "error";
public final static String STACKTRACE = "stacktrace";
private static PublishLock instance = new PublishLock(); private static PublishLock instance = new PublishLock();
private PublishLock() { private PublishLock() {
@ -79,7 +80,7 @@ public class PublishLock {
} }
} }
protected synchronized void setError(final ContentItem item) { protected synchronized void setError(final ContentItem item, final String stacktrace) {
SessionManager.getSession().getTransactionContext().beginTxn(); SessionManager.getSession().getTransactionContext().beginTxn();
final DataCollection collection = SessionManager.getSession().retrieve( final DataCollection collection = SessionManager.getSession().retrieve(
LOCK_OBJECT_TYPE); LOCK_OBJECT_TYPE);
@ -91,6 +92,7 @@ public class PublishLock {
final DataObject lock = collection.getDataObject(); final DataObject lock = collection.getDataObject();
lock.set(ACTION, ERROR); lock.set(ACTION, ERROR);
lock.set(STACKTRACE, stacktrace);
lock.save(); lock.save();
} }
collection.close(); collection.close();