![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Content Management Guide
CHAPTER 5
This chapter describes how tasks work in the Content Management (CM) subsystem and explains how to reconfigure installed tasks and write and implement custom tasks. It contains the following sections:
You also can use the CMS Administration Console to manage tasks. For more information, see Administering Automated Tasks.
An exteNd Director task is a background job or process that you can configure to run at a specified time or specified times. Typically, a task carries out a specific CM operation, such as publishing documents.
Using tasks A task must be enabled before it can be used in a deployed exteNd Director application. A list of enabled tasks appears in the Task section of the CMS Administration Console. You can start and stop the tasks that appear in this list while an application is running
Types of tasks There are two types of exteNd Director tasks: periodic and scheduled. Periodic tasks are configured to run at regular intervals (specified in milliseconds). Scheduled tasks are configured to run at specific dates and times. A task can be scheduled, periodic, or both.
The following tasks are installed with the CM subsystem:
Configurability These installed tasks are highly configurable (in a set of three XML files) and can be adjusted to meet the specific needs of your application. For example, you might provide a task such as the publisher or the janitor with a query that defines the scope of its operation. Such a query would specify the set of documents on which the task was to operate.
For information on which files you need to edit to reconfigure an installed task, see About how tasks are registered and configured. For an example, see Customizing an installed task.
You may not be able to meet the needs of some applications just by reconfiguring the installed tasks. In such cases you can also create new, application-specific tasks.
When you create a task, you:
Register its type, name, description, and configuration information
Create Java classes to provide the task's functionality and register these classes.
For information on the files you need to edit to register and configure a new task—and register the Java classes you create for it, see About how tasks are registered and configured next.
Tasks—and the Java classes associated with them—are registered and configured in three XML files in your project's library/ContentMgmtService/ContentMgmtService.spf/ContentMgmtService-conf directory:
The entries in tasktypes.xml establish the name and description of each task and identify each task as either periodic or scheduled (or both). The structure of this file must conform to framework-task-type_3_0.dtd in your project's library/FrameworkService/FrameworkService.spf/DTD directory.
Here is an excerpt from tasktypes.xml, showing how the file is structured and how the default, synch, and publish installed tasks are initially defined:
<framework-task-types> <!-- PERIODIC TASK TYPES --> <periodic> <task-type> <type-name>default</type-name> <type-descr>The Default Periodic Task</type-descr> </task-type> <task-type> <type-name>synch</type-name> <type-descr>Periodic CM/Search Engine Synchronization Task</type-descr> </task-type> <task-type> <type-name>publish</type-name> <type-descr>Periodic Document Publish Task</type-descr> </task-type> ... </periodic> <!-- SCHEDULED TASK TYPES --> <scheduled> ... </scheduled> </framework-task-types>
The entries in Default_tasklist.xml configure each task in conformance with contentmgmt-task-list_3_0.dtd in your project's library/ContentMgmtService/ContentMgmtService.spf/DTD directory.
Here is an excerpt from Default_tasklist.xml showing how the file is structured and how the periodic-publish task is configured:
<contentmgmt-task-list>
...
<periodic-publish>
<task-name>Default Repository Document Publish</task-name>
<description>The Default Repository Document Publish Task</description>
<since-last>false</since-last>
<enabled>true</enabled>
<interval>
<millis>86400000</millis>
<exact>false</exact>
</interval>
<do-all-not-yet-published>false</do-all-not-yet-published>
<do-all-unpublished>false</do-all-unpublished>
<do-all-ready>false</do-all-ready>
<force-publish>false</force-publish>
</periodic-publish>
...
</contentmgmt-task-list>
Naming convention Note that the tag name for the periodic-publish task is constructed from its type (periodic) and its name (publish) as defined in tasktypes.xml, connected by a hyphen. This is a required naming convention for the Default_tasklist.xml file.
Enabling or disabling a task
Note that to enable a task, you set the content of the <enabled>
tag to true. To disable a task, you set this value to false.
The services.xml file includes entries that associate tasks (and other exteNd Director functions) with their respective Java classes. The structure of this file must conform to framework-services_3_0.dtd in your project's library/FrameworkService/FrameworkService.spf/DTD directory.
Here is an excerpt from services.xml showing how the periodic-publish task is handled:
<service>
<interface>com.sssw.cm.periodic-publish</interface>
<impl-class>com.sssw.cm.task.impl.EboDocPeriodicPublishTask</impl-class>
<description>Periodic CM Document Publish Task</description>
<max-instances>0</max-instances>
<startup>M</startup>
<namespaced>false</namespaced>
</service>
Graphical view exteNd Director also provides a graphical view of this file where you can add new entries.
New tasks only You will need to add new entries to services.xml only if you create new tasks.
You customize an installed task by editing its configuration in the Default_tasklist.xml file.
In the following example, a document query has been added to the definition of the periodic-publish task. The query is specified in the <content-search>
element.
The added code (shown in bold) configures the periodic-publish task to publish all documents whose STATUS has been set to Reviewed:
<periodic-publish> <task-name>Default Repository Document Publish</task-name> <description>The Default Repository Document Publish Task</description> <since-last>false</since-last> <enabled>true</enabled> <interval> <millis>86400000</millis> <exact>false</exact> </interval> <do-all-not-yet-published>false</do-all-not-yet-published> <do-all-unpublished>false</do-all-unpublished> <do-all-ready>false</do-all-ready> <force-publish>false</force-publish> <content-search><where-clause>
<eq>
<var>STATUS</var> <val>Reviewed</val> </eq> </where-clause> </content-search> </periodic-publish>
For a complete description of the elements and values you can use to construct a document query within a task's definition, see the definition of the
<content-search>
element in contentmgmt-task-list_4_0.dtd.
Need to redeploy You must redeploy your application EAR for any task configuration changes to take effect.
The following procedure is based on the example of creating a new task named new-doc-notifier that checks for new documents and notifies a list of recipients about the new documents by e-mail.
To create and implement a new task:
To do so, modify the tasktypes.xml file. You can register the task as scheduled, periodic, or both scheduled and periodic. In this example, the new task is periodic:
<periodic> ............. <task-type> <type-name>new-doc-notifier</type-name> <type-descr>Periodic CM task for notifying of any new documents.</type-descr> </task-type>
Register your task in the tasklist.
To do so, add a new element to the Default_tasklist.xml file:
<periodic-new-doc-notifier> <task-name>New Document Notifier</task-name> <description> Periodic CM task for notifying of any new documents.</description> <since-last>false</since-last> <enabled>true</enabled> <interval> <millis>86400000</millis> <exact>false</exact> </interval> <!-- any other XML that is specific to the custom task goes here... --> <!-- for instance, there may be a node here defining the list of email recipients. --> <recipients> <recipient>user@myco.com</recipient> <recipient>user2@myco.com</recipient> <recipient>user3@myco.com</recipient> </recipients> <mail-smtp-host>smtp_host@myco.com</mail-smtp-host> <subject>New documents have been added</subject> <text>The following new documents have been added:</text> </periodic-new-doc-notifier>
Naming convention Note that the name of the XML tag surrounding the task definition (<periodic-new-doc-notifier>
) must be constructed from the task's type (periodic or scheduled) and the task's name in Default_tasklist.xml. This naming convention is required.
Write Java classes for the new task.
The generic exteNd Director task management API is provided in the com.sssw.fw.task.api package. This package contains very general interfaces for tasks, task types, and task management:
The CM subsystem subclasses those interfaces in its own task management package (com.sssw.cm.task.api). It provides its own EbiTask and EbiTaskManager along with EbiTaskMgmtDelegate, all three of which should be used for managing tasks. This package also contains generic interfaces for document publishing, expiration, removal, and synchronization between the CM subsystem and the Search subsystem engine.
When writing your own custom task, you should implement one of the following interfaces:
In the code for the new-doc-notifier example, the NewDocumentNotifier class extends com.sssw.cm.task.impl.EboTask and encapsulates the details of the task's duties and how they are carried out. The PeriodicNewDocumentNotifier class is the periodic subclass of the NewDocumentNotifier class.
For a complete listing of the Java code for the new-doc-notifier example, see Custom task sample code.
Register the new task's Java class.
To do so, add an entry to the services.xml file under <!-- Task management related objects -->
:
<!-- Periodic tasks --> ........................ <service> <interface>com.myco.cmtask.api.periodic-new-doc-notifier</interface> <impl-class>com.myco.cmtask.impl.PeriodicNewDocumentNotifier</impl-class> <description>The periodic new document notifier class.</description> <max-instances>0</max-instances> <startup>M</startup> </service>
Naming convention Note that in order for the object to be factoried and instantiated correctly, the interface naming should correspond to the task kind and type. For example, periodic and new-doc-notifier map to periodic-new-doc-notifier in the <interface>
node value.
Prepare for your custom task to be loaded and instantiated correctly:
Place your custom task class or classes into a separate JAR.
In the PMC WAR of your application, add the custom class JAR to the Class-Path section of the META-INF/MANIFEST.MF file.
This ensures that class loading works correctly and that users can manage the custom tasks in the Task section of the CMS Administration Console.
In a browser window, launch the CMS Administration Console and log in.
In the Tasks Pane, click to select your task and then click the Start button.
TIP: To stop a task, click the Stop button.
This section provides a listing of the Java code for the NewDocumentNotifier class discussed in Step 3 above.
This section also includes the code for the PeriodicNewDocumentNotifier class, which is the periodic subclass of the NewDocumentNotifier class.
package com.myco.cmtask.impl; // Java imports import java.io.*; import java.sql.Timestamp; import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; // FW imports import com.sssw.fw.api.*; import com.sssw.fw.exception.*; import com.sssw.fw.log.*; import com.sssw.fw.task.exception.*; import com.sssw.fw.util.*; // CM imports import com.sssw.cm.api.*; import com.sssw.cm.factory.*; import com.sssw.cm.task.api.*; import com.sssw.cm.task.impl.EboTask; // Other imports import org.w3c.dom.*; abstract public class NewDocumentNotifier extends EboTask { // // Constants // protected static final String RECIPIENTS = "recipients"; protected static final String RECIPIENT = "recipient"; protected static final String SMTP_HOST = "mail-smtp-host"; protected static final String SUBJECT = "subject"; protected static final String TEXT = "text"; protected static final String SENDER = "sender"; protected static final String NEWLINE = "\n"; protected static final String MAIL_SMTP_HOST = "mail.smtp.host"; protected static final String LINE_SEPARATOR = "line.separator"; // These actually belong in a resource bundle... protected static final String ERROR = "An error occurred while executing the New Document Notifier task."; protected static final String DEFAULT_SUBJECT = "New documents have been added"; protected static final String DEFAULT_TEXT = "The following documents have been added:"; protected static final String DEFAULT_SENDER = "notifier@myco.com"; protected static final String LOCATION = "Location: "; protected static final String TITLE = "Title: "; protected static final String AUTHOR = "Author: "; // // Member variables // protected EbiLog m_log; // Our log protected ArrayList m_recipients; // Notification recipients protected String m_smtpHost; // SMTP host protected String m_subject; // Message subject protected String m_text; // Message text protected String m_sender; // Sender protected String m_lineSep; // Line separator // Constructor public NewDocumentNotifier() { // Use the CM log m_log = EboLogFactory.getLog(EboLogFactory.CM); m_recipients = new ArrayList(); m_subject = DEFAULT_SUBJECT; m_text = DEFAULT_TEXT; m_sender = DEFAULT_SENDER; } // Initialization from XML public void fromXML(Node node) { // Rely on the superclass to get all the general task // settings super.fromXML(node); try { NodeList nodes = node.getChildNodes(); if (nodes != null) { // Process the nodes for (int i = 0; i < nodes.getLength(); i++) { Node child = nodes.item(i); String nodeName = child.getNodeName(); if (child.getNodeType() == Node.ELEMENT_NODE) { // Recipient list if (RECIPIENTS.equals(nodeName)) processRecipientList(child); // SMTP host else if (SMTP_HOST.equals(nodeName)) m_smtpHost = getElementValue(child); // Message subject else if (SUBJECT.equals(nodeName)) m_subject = getElementValue(child); // Base message text else if (TEXT.equals(nodeName)) m_text = getElementValue(child); // Sender else if (SENDER.equals(nodeName)) m_sender = getElementValue(child); } } // End for each node } } catch (Exception ex) { EboExceptionHelper.handleException( ex, // The exception m_log, // Our log to write exception into false, // Don't print stack trace to console false); // Don't rethrow as a runtime exception } } // Process the list of recipients provided in the XML task definition protected void processRecipientList(Node node) { NodeList nodes = node.getChildNodes(); if (nodes != null) { // Process the nodes for (int i = 0; i < nodes.getLength(); i++) { Node child = nodes.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { String nodeName = child.getNodeName(); if (RECIPIENT.equals(nodeName)) { String recipient = getElementValue(child); if (!EboStringMisc.isEmpty(recipient)) m_recipients.add(recipient); } } } } } // Extract a node value from a Node public static String getElementValue(Node node) { // Entities are often considered separate text nodes; // for example, Jim's wagon is represented by three // text nodes "Jim", "'",and "s wagon". Thus all // children need to be concatenated in order to retrieve // the proper text node value. String nodeValue; if (node.hasChildNodes()) { Node curNode = node.getFirstChild(); nodeValue = EboStringMisc.m_emptyStr; while (curNode != null) { nodeValue = nodeValue + curNode.getNodeValue(); curNode = curNode.getNextSibling(); } } else nodeValue = EboStringMisc.m_emptyStr; return nodeValue; } // Carry out the task public void doTask() throws EboTaskException { try { super.doTask(); EbiContentManager cmgr = EboFactory.getDefaultContentManager(); EbiDocQuery query = (EbiDocQuery)cmgr.createQuery(EbiDocQuery.DOC_QUERY); // If we're to only get the data that's changed since // The time that the task was last run if (getSinceLast()) { // Figure out the start of the interval Timestamp fromTime = getFromTime(); // Figure out the end of the interval Timestamp toTime = new Timestamp((new Date()).getTime()); EbiQueryExpression expr = null; EbiQueryExpression expr2 = null; // Augment the where clause with the time interval if (fromTime != null) expr = query.whereCreateDate(fromTime, EbiDocQuery.ROP_GREATER, false); if (toTime != null) expr2 = query.whereCreateDate(toTime, EbiDocQuery.ROP_LEQ, false); // Set the augmented where clause into the query if (expr != null && expr2 != null) { expr.andExpression(expr2); query.setWhere(expr); } } // Otherwise, we'll process all the documents // Get the list of documents Collection documents = cmgr.findElementsFiltered(m_context, query); // Send the e-mail notifications sendNotifications(documents); } catch (Exception ex) { throw new com.sssw.fw.task.exception.EboTaskException(ex, ERROR); } } // Send the e-mail notifications to our recipients protected void sendNotifications(Collection documents) throws EboUnrecoverableSystemException, EboSecurityException, MessagingException { if (!documents.isEmpty()) { String msgText = getEmailMessageBody(documents); // For each recipient for (int i = 0; i < m_recipients.size(); i++) { String recipient = (String)m_recipients.get(i); send( m_sender, // From recipient, // To m_smtpHost, // Host m_subject, // Subject msgText); // Yext } } } // Generate an e-mail // "The following documents have been added: // // <doc 1> // <doc 2> // ....... // <doc N>" protected String getEmailMessageBody(Collection documents) throws EboUnrecoverableSystemException, EboSecurityException { String lineSeparator = getLineSeparator(); StringBuffer buf = new StringBuffer(m_text); buf.append(lineSeparator); buf.append(lineSeparator); Iterator iter = documents.iterator(); while (iter.hasNext()) { EbiDocument doc = (EbiDocument)iter.next(); buf.append(getDocumentDescriptor(doc)); buf.append(lineSeparator); buf.append(lineSeparator); } return buf.toString(); } // Send an e-mail protected static void send( String from, String to, String host, String subject, String msgText) throws MessagingException { Properties props = System.getProperties(); props.put(MAIL_SMTP_HOST, host); Session session = Session.getDefaultInstance(props, null); // Create a message Message msg = new MimeMessage(session); msg.setFrom(new InternetAddress(from)); InternetAddress[] address = { new InternetAddress(to) }; msg.setRecipients(Message.RecipientType.TO, address); msg.setSubject(subject); msg.setSentDate(new Date()); msg.setText(msgText); Transport.send(msg); } // Generate a document descriptor // Location: <...> // Title: <...> // Author: <...> protected String getDocumentDescriptor(EbiDocument doc) throws EboUnrecoverableSystemException, EboSecurityException { String lineSeparator = getLineSeparator(); StringBuffer buf = new StringBuffer(LOCATION); buf.append(doc.getURL(false)); buf.append(lineSeparator); buf.append(TITLE); buf.append(doc.getTitle()); buf.append(lineSeparator); buf.append(AUTHOR); buf.append(doc.getAuthor()); return buf.toString(); } // Figure out the line separator to use protected String getLineSeparator() { if (m_lineSep == null) m_lineSep = System.getProperty(LINE_SEPARATOR, NEWLINE); return m_lineSep; } abstract protected Timestamp getFromTime(); }
package com.myco.cmtask.impl; // Java imports import java.sql.Timestamp; // Framework imports import com.sssw.fw.task.api.*; import com.sssw.fw.task.impl.*; // CM imports import com.sssw.cm.api.*; import com.sssw.cm.task.api.*; // Other imports import org.w3c.dom.*; public class PeriodicNewDocumentNotifier extends NewDocumentNotifier implements EbiPeriodicTask { // // Protected data // protected long m_interval; // Interval, if any protected boolean m_exact; // Run asap or x millis after // current time // // Constructor // public PeriodicNewDocumentNotifier() { } public boolean isExact() { return m_exact; } public long getInterval() { return m_interval; } public void setExact(boolean exact) { m_exact = exact; } public void setInterval(long millis) { m_interval = millis; } public void fromXML(Node node) { super.fromXML(node); EboTaskHelper.getPeriodicDataFromXML(this, node); } public String toString() { return super.toString() + ", Interval (millis)=" + m_interval + ", Exact=" + m_exact; } protected Timestamp getFromTime() { // For an interval-based task, the 'from' time is 'none' if // the task has not run once yet; otherwise it's // task_first_scheduled_time + interval*times_task_ran return (m_timesRan < 1) ? null : new Timestamp( m_launchTime.getTime() + m_interval * (m_timesRan - 1)); } }
Task events are an extension of the exteNd Director event model framework, consisting of state change events, event producers, and event listeners (including vetoable listeners). This section includes these topics:
This section assumes familiarity with exteNd Director event model and event handling. For more information, see the section on working with events in Developing exteNd Director Applications.
The API defines a set of state change events related to task management operations. Event IDs are exposed on the individual event classes as well as on the com.sssw.fw.task.event.api.EbiConstants interface:
Generic state change events In addition, there are generic state change constants representing types of changes defined in com.sssw.fw.event.api.EboStateChangeEvent.
To register a task event listener:
Use either the addStateChangeListener() or the addVetoableStateChangeListener method on the task manager object (com.sssw.cm.task.api.EbiTaskMgmtDelegate).
You can register for a specified type or types of events using this version of addStateChangeListener():
public boolean addStateChangeListener(
BitSet events, EbiStateChangeListener listener)
where events is a bit set of event IDs.
Use the event IDs specified in com.sssw.fw.event.api.EbiConstants. For example, this code registers for the task started, stopped, and completed operations:
EbiTaskMgmtDelegate tmgr = new EbiTaskMgmtDelegate(); EbiStateChangeListener listener = new EbiStateChangeListener(); // Instantiate a Java BitSet and populate it BitSet events = new BitSet(); events.set(EbiConstants.EVENT_ID_TASK_STARTED); events.set(EbiConstants.EVENT_ID_TASK_STOPPED); events.set(EbiConstants.EVENT_ID_TASK_COMPLETED); // add listener tmgr.addStateChangeListener(events, listener);
To enable or disable task events:
Open the config.xml for the Framework subsystem in your exteNd Director project.
com.sssw.fw.task.events.enable
Copyright © 2004 Novell, Inc. All rights reserved. Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved. more ...