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"
name="ccm-cms"
prettyName="Red Hat CCM Content Management System"
version="6.6.10"
version="6.6.11"
release="1"
webapp="ROOT">
<ccm:dependencies>

View File

@ -8,6 +8,7 @@ object type PublishLock {
String[0..1] lockedOid = cms_publish_lock.locked_oid VARCHAR(2048);
Date[0..1] timestamp = cms_publish_lock.lock_timestamp TIMESTAMP;
String[0..1] action = cms_publish_lock.action VARCHAR(256);
String[0..1] stacktrace = cms_publish_lock.stacktrace CLOB;
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. -->
<script sql="ccm-cms/upgrade/::database::-6.6.9-6.6.10.sql"/>
</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>

View File

@ -75,10 +75,9 @@ import java.text.DateFormat;
import org.apache.log4j.Logger;
/**
* 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
@ -117,10 +116,10 @@ class ItemLifecycleItemPane extends BaseItemPane {
if ((item == null) || item.getLastModifiedDate() == null) {
dateStr = "";
} else {
dateStr =
DateFormat.getDateTimeInstance(DateFormat.LONG,
dateStr = DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.SHORT,
GlobalizationHelper.getNegotiatedLocale()).format(item.
GlobalizationHelper.
getNegotiatedLocale()).format(item.
getLastModifiedDate());
}
final String msg = String.format(
@ -131,6 +130,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
dateStr);
label.setLabel(msg);
}
});
m_detailPane.add(lastPublishedLabel);
@ -166,9 +166,9 @@ class ItemLifecycleItemPane extends BaseItemPane {
final java.util.List props = super.properties(state);
final Lifecycle cycle = m_lifecycle.getLifecycle(state);
final DateFormat format =
DateFormat.getDateTimeInstance(DateFormat.FULL,
ContentSection.getConfig().getHideTimezone()
final DateFormat format = DateFormat.getDateTimeInstance(DateFormat.FULL,
ContentSection.getConfig().
getHideTimezone()
? DateFormat.SHORT
: DateFormat.FULL);
@ -189,6 +189,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
return props;
}
}
}
@ -229,6 +230,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
s_log.debug("User cannot publish " + item.getOID());
}
}
}
private class UnpublishLink extends PublishLink {
@ -254,6 +256,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
throw new RedirectSignal(target, true);
}
}
}
@ -293,7 +296,11 @@ class ItemLifecycleItemPane extends BaseItemPane {
@Override
public void uncaughtException(final Thread thread,
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(
"An error occurred while "
+ "publishing the item '%s': ",
@ -334,8 +341,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
traceWriter);
ex.printStackTrace(printWriter);
final Notification notification =
new Notification(
final Notification notification = new Notification(
sender,
receiver,
String.format(
@ -350,6 +356,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
notification.save();
}
}
});
thread.start();
@ -371,6 +378,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
}
}
}
}
/**
@ -379,11 +387,10 @@ class ItemLifecycleItemPane extends BaseItemPane {
private class Republisher implements Runnable {
/**
* 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.
* 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.
*/
private final String itemOid;
private final User user;
@ -395,11 +402,13 @@ class ItemLifecycleItemPane extends BaseItemPane {
@Override
public void run() {
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(itemOid));
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(
itemOid));
PublishLock.getInstance().lock(item);
republish(item, false, user);
PublishLock.getInstance().unlock(item);
}
}
}
@ -424,8 +433,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
final User user = Web.getContext().getUser();
/**
* 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, user);
@ -434,7 +442,11 @@ class ItemLifecycleItemPane extends BaseItemPane {
@Override
public void uncaughtException(final Thread thread,
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(
"An error occurred while "
+ "publishing the item '%s': ",
@ -475,8 +487,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
traceWriter);
ex.printStackTrace(printWriter);
final Notification notification =
new Notification(
final Notification notification = new Notification(
sender,
receiver,
String.format(
@ -491,6 +502,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
notification.save();
}
}
});
thread.start();
@ -512,6 +524,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
}
}
}
}
/**
@ -520,11 +533,10 @@ class ItemLifecycleItemPane extends BaseItemPane {
private class Republisher implements Runnable {
/**
* 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.
* 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.
*/
private final String itemOid;
private final User user;
@ -536,11 +548,13 @@ class ItemLifecycleItemPane extends BaseItemPane {
@Override
public void run() {
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(itemOid));
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(
itemOid));
PublishLock.getInstance().lock(item);
republish(item, true, user);
PublishLock.getInstance().unlock(item);
}
}
}
@ -554,6 +568,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
group.setSubject(new PhaseTable());
}
}
private class PhaseTable extends Table {
@ -567,11 +582,11 @@ class ItemLifecycleItemPane extends BaseItemPane {
lz("cms.ui.item.lifecycle.end_date")
});
}
}
/**
* 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
*/
@ -580,8 +595,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
implements FormProcessListener,
FormInitListener {
private static final String LIFECYCLE_ACTION =
"itemLifecycleItemPaneActionSelect";
private static final String LIFECYCLE_ACTION = "itemLifecycleItemPaneActionSelect";
private static final String REPUBLISH = "republish";
private static final String UNPUBLISH = "unpublish";
private static final String REPUBLISH_AND_RESET = "republishAndReset";
@ -648,8 +662,8 @@ 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()) {
@ -659,7 +673,11 @@ class ItemLifecycleItemPane extends BaseItemPane {
@Override
public void uncaughtException(final Thread thread,
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(
"An error occurred while "
+ "publishing the item '%s': ",
@ -700,8 +718,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
traceWriter);
ex.printStackTrace(printWriter);
final Notification notification =
new Notification(
final Notification notification = new Notification(
sender,
receiver,
String.format(
@ -716,6 +733,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
notification.save();
}
}
});
thread.start();
@ -736,14 +754,17 @@ class ItemLifecycleItemPane extends BaseItemPane {
}
} else if (REPUBLISH_AND_RESET.equals(selected)) {
if (CMSConfig.getInstance().getThreadedPublishing()) {
final RepublishAndResetRunner runner =
new RepublishAndResetRunner(item, user);
final RepublishAndResetRunner runner = new RepublishAndResetRunner(item, user);
final Thread thread = new Thread(runner);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread thread,
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(
"An error occurred while "
+ "publishing the item '%s': ",
@ -784,8 +805,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
traceWriter);
ex.printStackTrace(printWriter);
final Notification notification =
new Notification(
final Notification notification = new Notification(
sender,
receiver,
String.format(
@ -800,6 +820,7 @@ class ItemLifecycleItemPane extends BaseItemPane {
notification.save();
}
}
});
thread.start();
@ -828,11 +849,10 @@ class ItemLifecycleItemPane extends BaseItemPane {
private class RepublishRunner implements Runnable {
/**
* 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.
* 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.
*/
private final String itemOid;
private final User user;
@ -843,27 +863,29 @@ class ItemLifecycleItemPane extends BaseItemPane {
}
private void doRepublish() {
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(itemOid));
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(
itemOid));
republish(item, false, user);
}
@Override
public void run() {
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(itemOid));
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(
itemOid));
PublishLock.getInstance().lock(item);
doRepublish();
PublishLock.getInstance().unlock(item);
}
}
private class RepublishAndResetRunner implements Runnable {
/**
* 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.
* 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.
*/
private final String itemOid;
private final User user;
@ -874,17 +896,20 @@ class ItemLifecycleItemPane extends BaseItemPane {
}
private void doRepublishAndReset() {
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(itemOid));
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(
itemOid));
republish(item, true, user);
}
@Override
public void run() {
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(itemOid));
final ContentItem item = (ContentItem) DomainObjectFactory.newInstance(OID.valueOf(
itemOid));
PublishLock.getInstance().lock(item);
doRepublishAndReset();
PublishLock.getInstance().unlock(item);
}
}
}
}

View File

@ -86,7 +86,8 @@ import javax.servlet.http.HttpServletRequest;
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 Xixi D'moon &lt;xdmoon@redhat.com&gt;
@ -96,8 +97,7 @@ import org.apache.log4j.Logger;
*/
class ItemLifecycleSelectForm extends BaseForm {
private static final Logger s_log =
Logger.getLogger(ItemLifecycleSelectForm.class);
private static final Logger s_log = Logger.getLogger(ItemLifecycleSelectForm.class);
private final static String LIFECYCLE = "lifecycle";
private final static String START_DATE = "start_date";
private final static String END_DATE = "end_date";
@ -133,9 +133,7 @@ class ItemLifecycleSelectForm extends BaseForm {
addField(gz("cms.ui.item.lifecycle"), m_cycleSelect);
// Start date
m_startDate =
new Date(new DateParameter(START_DATE) {
m_startDate = new Date(new DateParameter(START_DATE) {
@Override
protected final Calendar getCalendar(final HttpServletRequest sreq) {
@ -145,16 +143,15 @@ class ItemLifecycleSelectForm extends BaseForm {
return cal;
}
});
addField(gz("cms.ui.item.lifecycle.start_date"), m_startDate);
// Start time
final BoxPanel startTime = new BoxPanel(BoxPanel.HORIZONTAL);
addField(gz("cms.ui.item.lifecycle.start_time"), startTime);
// Hour
m_startHour = new TextField(new IntegerParameter("start_hour"));
startTime.add(m_startHour);
@ -163,7 +160,6 @@ class ItemLifecycleSelectForm extends BaseForm {
new NumberInRangeValidationListener(1, 12));
// Minute
m_startMinute = new TextField(new IntegerParameter("start_minute"));
startTime.add(m_startMinute);
@ -172,7 +168,6 @@ class ItemLifecycleSelectForm extends BaseForm {
0, 59));
// AM/PM
m_startAmpm = new SingleSelect(new IntegerParameter("start_ampm"));
startTime.add(m_startAmpm);
@ -180,13 +175,10 @@ class ItemLifecycleSelectForm extends BaseForm {
m_startAmpm.addOption(new Option("1", "pm"));
// Time zone
startTime.add(new Label(new TimeZonePrinter()));
// Expiration date
m_endDate =
new Date(new DateParameter(END_DATE) {
m_endDate = new Date(new DateParameter(END_DATE) {
@Override
protected final Calendar getCalendar(final HttpServletRequest sreq) {
@ -196,16 +188,15 @@ class ItemLifecycleSelectForm extends BaseForm {
return cal;
}
});
addField(gz("cms.ui.item.lifecycle.end_date"), m_endDate);
// End time
final BoxPanel endTime = new BoxPanel(BoxPanel.HORIZONTAL);
addField(gz("cms.ui.item.lifecycle.end_time"), endTime);
// Hour
m_endHour = new TextField(new IntegerParameter("end_hour"));
endTime.add(m_endHour);
@ -214,7 +205,6 @@ class ItemLifecycleSelectForm extends BaseForm {
12));
// Minute
m_endMinute = new TextField(new IntegerParameter("end_minute"));
endTime.add(m_endMinute);
@ -223,7 +213,6 @@ class ItemLifecycleSelectForm extends BaseForm {
new NumberInRangeValidationListener(0, 59));
// AM/PM
m_endAmpm = new SingleSelect(new IntegerParameter("end_ampm"));
endTime.add(m_endAmpm);
@ -232,12 +221,9 @@ class ItemLifecycleSelectForm extends BaseForm {
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_notificationHours =
new TextField(new IntegerParameter(NOTIFICATION_HOURS));
m_notificationHours = new TextField(new IntegerParameter(NOTIFICATION_HOURS));
m_notificationHours.setSize(4);
SimpleContainer cont = new SimpleContainer();
cont.add(m_notificationDays);
@ -251,12 +237,9 @@ class ItemLifecycleSelectForm extends BaseForm {
// A hidden field that checks to see if the user wants publish
// with a start time earlier than current time.
addAction(new Submit("finish", gz("cms.ui.item.lifecycle.publish")));
// Form listeners
addValidationListener(new ValidationListener());
addSecurityListener(PUBLISH, m_item);
addInitListener(new InitListener());
@ -267,11 +250,9 @@ class ItemLifecycleSelectForm extends BaseForm {
@Override
public final void prepare(final PrintEvent e) {
final ContentSection section =
CMS.getContext().getContentSection();
final ContentSection section = CMS.getContext().getContentSection();
final LifecycleDefinitionCollection ldc =
section.getLifecycleDefinitions();
final LifecycleDefinitionCollection ldc = section.getLifecycleDefinitions();
ldc.addOrder("label");
final SingleSelect target = (SingleSelect) e.getTarget();
@ -283,7 +264,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// XXX domlay this seems a little weak. perhaps
// there's a better way to determine if a lifecycle is
// ready to be applied to an item.
if (!pdc.isEmpty()) {
target.addOption(new Option(ld.getID().toString(),
ld.getLabel()));
@ -294,6 +274,7 @@ class ItemLifecycleSelectForm extends BaseForm {
ldc.close();
}
}
private class InitListener implements FormInitListener {
@ -308,17 +289,14 @@ class ItemLifecycleSelectForm extends BaseForm {
// If the item is published, select the currently
// associated lifecycle.
final LifecycleDefinition ld =
item.getLifecycle().
final LifecycleDefinition ld = item.getLifecycle().
getLifecycleDefinition();
m_cycleSelect.setValue(state, ld.getID());
} else {
// Set the default lifecycle (if it exists).
final ContentSection section =
CMS.getContext().getContentSection();
final LifecycleDefinition ld =
ContentTypeLifecycleDefinition.
final ContentSection section = CMS.getContext().getContentSection();
final LifecycleDefinition ld = ContentTypeLifecycleDefinition.
getLifecycleDefinition(section, item.getContentType());
if (ld != null) {
@ -327,7 +305,6 @@ class ItemLifecycleSelectForm extends BaseForm {
}
// Set the default start date.
// XXX Isn't just new Date() sufficient?
final java.util.Date start = new java.util.Date(System.
currentTimeMillis());
@ -339,7 +316,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// If the hour is 12, then Calendar.get(Calendar.HOUR)
// returns 0 (from the 24 hour time - 12). We want it to
// return 12.
if (calendar.get(Calendar.HOUR) == 0) {
m_startHour.setValue(state, new Integer(12));
} else {
@ -358,22 +334,21 @@ class ItemLifecycleSelectForm extends BaseForm {
m_startAmpm.setValue(state,
new Integer(calendar.get(Calendar.AM_PM)));
BigInteger[] defaultTime =
BigInteger.valueOf(ContentSection.getConfig().
BigInteger[] defaultTime = BigInteger.valueOf(ContentSection.getConfig().
getDefaultNotificationTime()).
divideAndRemainder(BigInteger.valueOf(24));
m_notificationDays.setValue(state, new Integer(defaultTime[0].
intValue()));
m_notificationHours.setValue(state, new Integer(defaultTime[1].
intValue()));
}
}
/**
* jensp 2011-12-14: Some larger changes to the behavior of the process
* listener. The real action has been moved to the
* 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
@ -397,6 +372,7 @@ class ItemLifecycleSelectForm extends BaseForm {
publisher.publish();
PublishLock.getInstance().unlock(item);
}
};
final Thread thread = new Thread(threadAction);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@ -404,7 +380,11 @@ class ItemLifecycleSelectForm extends BaseForm {
@Override
public void uncaughtException(final Thread thread,
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(
"An error occurred while "
+ "publishing the item '%s': ",
@ -459,6 +439,7 @@ class ItemLifecycleSelectForm extends BaseForm {
notification.save();
}
}
});
thread.start();
} else {
@ -603,6 +584,7 @@ class ItemLifecycleSelectForm extends BaseForm {
* Utilities.getWorkspaceURL()), true); }
*/
}
}
/**
@ -716,8 +698,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.
@ -740,7 +722,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// XXX domlay Whoa. This must be broken for multiphase
// lifecycles.
if (endDate != null) {
// update individual phases
@ -780,14 +761,14 @@ class ItemLifecycleSelectForm extends BaseForm {
}
if (notificationPeriod > 0) {
notificationDate =
computeNotificationDate(endOfCycle, notificationPeriod);
notificationDate = computeNotificationDate(endOfCycle, notificationPeriod);
s_log.debug("adding custom phase");
Phase expirationImminentPhase =
lifecycle.addCustomPhase("expirationImminent",
new Long(notificationDate.
Phase expirationImminentPhase = lifecycle.addCustomPhase("expirationImminent",
new Long(
notificationDate.
getTime()),
new Long(endOfCycle.getTime()));
new Long(endOfCycle.
getTime()));
expirationImminentPhase.setListenerClassName(
"com.arsdigita.cms.lifecycle.NotifyLifecycleListener");
expirationImminentPhase.save();
@ -802,8 +783,7 @@ class ItemLifecycleSelectForm extends BaseForm {
item.save();
if (workflowOid != null) {
final Workflow workflow =
(Workflow) DomainObjectFactory.newInstance(OID.
final Workflow workflow = (Workflow) DomainObjectFactory.newInstance(OID.
valueOf(workflowOid));
try {
finish(workflow, item, user);
@ -812,6 +792,7 @@ class ItemLifecycleSelectForm extends BaseForm {
}
}
}
}
static void finish(Workflow workflow, ContentItem item, User user) throws
@ -876,8 +857,7 @@ class ItemLifecycleSelectForm extends BaseForm {
"cms.ui.item.lifecycle.start_date_invalid"));
}
java.util.Date nowDate =
new java.util.Date(System.currentTimeMillis());
java.util.Date nowDate = new java.util.Date(System.currentTimeMillis());
Calendar cStart = Calendar.getInstance();
Calendar cNow = Calendar.getInstance();
@ -900,7 +880,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// Give the user extra 5 minutes before form complains
// start time's in the past.
cStart.set(Calendar.MINUTE, startMinute.intValue() + 5);
cStart.set(Calendar.AM_PM, startAmpm.intValue());
cStart.set(Calendar.SECOND, cNow.get(Calendar.SECOND));
@ -913,8 +892,7 @@ class ItemLifecycleSelectForm extends BaseForm {
Integer endHour = (Integer) m_endHour.getValue(state);
Integer endMinute = (Integer) m_endMinute.getValue(state);
java.util.Date endDate =
(java.util.Date) m_endDate.getValue(state);
java.util.Date endDate = (java.util.Date) m_endDate.getValue(state);
if (endHour == null && (endMinute != null || endDate != null)) {
throw new FormProcessException(lz(
@ -954,7 +932,6 @@ class ItemLifecycleSelectForm extends BaseForm {
// Give the user extra 5 minutes before form complains
// end time's in the past.
cEnd.set(Calendar.MINUTE, endMinute.intValue() + 5);
cEnd.set(Calendar.AM_PM, endAmpm.intValue());
cEnd.set(Calendar.SECOND, cNow.get(Calendar.SECOND));
@ -966,10 +943,8 @@ class ItemLifecycleSelectForm extends BaseForm {
"cms.ui.item.lifecycle.end_date_before_start_date"));
}
Integer notificationDays =
(Integer) m_notificationDays.getValue(state);
Integer notificationHours =
(Integer) m_notificationHours.getValue(state);
Integer notificationDays = (Integer) m_notificationDays.getValue(state);
Integer notificationHours = (Integer) m_notificationHours.getValue(state);
int notificationPeriod = 0;
if (notificationDays != null) {
@ -981,8 +956,7 @@ class ItemLifecycleSelectForm extends BaseForm {
if (notificationPeriod > 0) {
// point in time for notification == end date - notificationPeriod
java.util.Date notificationDate =
computeNotificationDate(cEnd.getTime(),
java.util.Date notificationDate = computeNotificationDate(cEnd.getTime(),
notificationPeriod);
s_log.debug("cStart (Date): " + cStart.getTime());
s_log.debug("notificationDate: " + notificationDate);
@ -1000,6 +974,7 @@ class ItemLifecycleSelectForm extends BaseForm {
}
}
}
}
public class TimeZonePrinter implements PrintListener {
@ -1018,24 +993,22 @@ class ItemLifecycleSelectForm extends BaseForm {
mStart.setTime((java.util.Date) m_startDate.getValue(state));
}
final String zone =
mStart.getTimeZone().getDisplayName(true,
final String zone = mStart.getTimeZone().getDisplayName(true,
TimeZone.SHORT);
target.setLabel(zone);
}
}
}
/**
* 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.
* 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
* @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) {
@ -1046,4 +1019,5 @@ class ItemLifecycleSelectForm extends BaseForm {
return new java.util.Date(endDate.getTime() - (long) notificationPeriod
* 3600000L);
}
}

View File

@ -22,6 +22,7 @@ public class PublishLock {
public final static String TIMESTAMP = "timestamp";
public final static String ACTION = "action";
public final static String ERROR = "error";
public final static String STACKTRACE = "stacktrace";
private static PublishLock instance = new 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();
final DataCollection collection = SessionManager.getSession().retrieve(
LOCK_OBJECT_TYPE);
@ -91,6 +92,7 @@ public class PublishLock {
final DataObject lock = collection.getDataObject();
lock.set(ACTION, ERROR);
lock.set(STACKTRACE, stacktrace);
lock.save();
}
collection.close();