Introduction
Document and Publication Job Plugins allow to implement custom functionality in the Publication Planner workflow engine and make them available in the download dialog for a publication or document selection.
Basically, two types of jobs are available: DocumentJobs and PublicationJobs. The following overview lists the main specifics of these job types:
DocumentJob | PublicationJob |
---|---|
Can be integrated in Document workflows | Can be integrated in Publication workflows and Download dialog |
Operate on single documents | Operate on a publication, on several publications or mixed selections (publications and documents). |
Preparation of documents (close, create, recreate, open and closing after job execution) managed by the Rendering Server. | Affected documents are locked by the Rendering Server during job execution, all other preparation must be implemented in the job or in the target script executed by the renderer. After job execution, documents are unlocked and closed. |
It is not allowed to open / close documents in the job implementation or target renderer scripts | It is explicitly allowed to open and close certain documents in the job implementation or target scripts |
Implementation requires
|
Implementation requires
|
This Howto covers a simple implementation example for either type of job. Detailed information about relevant Java interfaces and cscript resources can be found in the PubServerSDK documentation resp. InDesign Plugin documentation.
Note:
When implementing workflow jobs for publications or documents or additional download functionality without rendering interaction, you only need to implement the "Plugin" part (no DocumentJob resp. PublicationJob implementation). Though, this documentation may help to clarify some questions.
Requirements
Implementing document and publication jobs as described in this documentation requires
- priint:publishing server 4.1.5 Build >= #1007 or
- priint:publishing server 4.1 Build >= #5162
- priint:comet ID plug-ins 4.1 / 4.1.5 >= R22160
Document Jobs Example
DocumentJobs are executed during workflow processing for a single document.
Implementation of a DocumentJob requires
- a Job Plugin with a PubServerMethod with MethodType=WORKFLOW_JOB
- a DocumentJob implementation and
- (optionally) a cscript, which is executed by the renderer.
The example shown below is also part of the DemoDocumentJob project available on the documentation site.
Plugin Implementation
We start with the PubServer Plugin for our new DocumentJob.
Basically, this is just a PubServer Plugin (i.e. PluginControl implementation), though some additional methods and the tagging interface RenderingPlugin must be implemented. For ease of implementation, we inherit from the AbstractDocumentJobPlugin class defined in CometServer4SDK.
In the workflow job context, the Plugin implementation serves as connector for the UI and job scheduling enginge.
package com.priint.pubserver.pubplanner.demo.document; import java.util.Map; import javax.ejb.LocalBean; import javax.ejb.Stateless; import com.priint.pubserver.comet.renderer.DocumentJob; import com.priint.pubserver.comet.renderer.legacy.AbstractDocumentJobPlugin; import com.priint.pubserver.exception.PubServerException; import com.priint.pubserver.plugin.PluginMethod; import com.priint.pubserver.plugin.annotation.PubServerMethod; import com.priint.pubserver.plugin.annotation.PubServerMethodParameter; import com.priint.pubserver.plugin.annotation.PubServerPlugin; import com.werkii.server.PubPlannerPlugins; @Stateless(mappedName=SyncPlaceholderJobPlugin.MAPPED_NAME) @LocalBean @PubServerPlugin public class SyncPlaceholderJobPlugin extends AbstractDocumentJobPlugin { public static final String MAPPED_NAME = "com.priint.pubserver.pubplanner.demo.document.SyncPlaceholderJobPlugin"; @Override public DocumentJob getDocumentJob(Map<String, Object> parameters) { return new SyncPlaceholderJob(); } @PubServerMethod( label="name", type=PluginMethod.MethodType.GENERIC, description="plugin method called to obtain a translation of plugin label") public String getMethodName( @PubServerMethodParameter( name="lang", defaultValue=PubPlannerPlugins.EN, listOfValues={}) final String lang) { return "Synchronize placeholders"; } @PubServerMethod( label="Synchronize placeholders", type=PluginMethod.MethodType.WORKFLOW_JOB) public void exec( @PubServerMethodParameter( name="parameters") final Map<String,Object> parameters) throws PubServerException { // just call the super class exec method: super.exec(parameters); } }
Remarks
- the
getDocumentJob
method is part of the AbstractDocumentJobPlugin contract and must return a DocumentJob implementation.
If your job implementation is dependent on parameters set in the UI or environment values (such as the current selection), parameters should be avaluated in this method and passed to the DocumentJob implementation e.g. in the constructor. - the
getMethodName
method should return the name of this method as shown in the UI - the
exec
method is part of the RenderingPlugin contract. The method name is not obligatory, but any method of this Plugin with type=WORKFLOW_JOB must accept one parameter of type Map<String,Object>.
It is possible to define more than one WORKFLOW_JOB method in one Job Plugin, though in the UI they all would appear with the same name - therefore it is not recommended doing so.You should implement only one method with MethodType=WORKFLOW_JOB in a PubServer Plugin
- we use the super class implementation of the
exec
method, which in most cases (when a renderer is involved and the actual job is implemented in the Job class) should serve our requirements.
Though, we must explicitly override the method in the Plugin implementation, otherwise the method will not be registered by the PluginManager and therefore will not be available in the Publication Planner - in this example, the
getParameters
method is just inherited from the abstract super class (which returns no parameters). See the PublicationJob implementation below for an example how to implement custom parameters for jobs.
Job Implementation
Job implementations represent the business logic for an actual server job. They must implement the com.priint.pubserver.comet.renderer.DocumentJob
interface, or - as shown below - use default implementations and inherit from com.priint.pubserver.comet.renderer.DocumentJobImpl
.
package com.priint.pubserver.pubplanner.demo.document; import java.io.File; import javax.activation.DataHandler; import com.priint.pubserver.comet.bridge.entitydata.RecordId; import com.priint.pubserver.comet.renderer.Comet; import com.priint.pubserver.comet.renderer.DocumentJobImpl; import com.priint.pubserver.comet.renderer.Options; import com.priint.pubserver.comet.renderer.Renderer.DocumentTarget; import com.priint.pubserver.comet.renderer.Renderer.Target; import com.priint.pubserver.exception.PubServerException; public class SyncPlaceholderJob extends DocumentJobImpl { @Override public DataHandler executeJob(String documentId, String documentPath, Comet renderer) throws PubServerException { DocumentTarget target = Target.document(documentId, new File(documentPath)); int pages = renderer.documentGetPageCount(target, Options.NO_OPTIONS); int[] placeholderIds = new int[0]; for (int index = 0; index < pages; ++index) { renderer.syncPlaceholders( target.page(index), placeholderIds, RecordId.getEmpty(), Options.NO_OPTIONS); } return ""; } }
What will happen?
This job iterates all pages and synchronizes all placeholders (empty placeholder list means: all).
All the document handling is done by the RenderingServer, we don't have to (and actually must not) open, close, delete or create the document in our job implementation.
Remarks
- the
executeJob
method is part of the DocumentJob contract. When this method is called, all preparation (creating, recreating, opening, locking the document etc.) has been done and a suitable renderer instance has been allocated. Typically, this renderer instance will be used to execute a custom cscript (like shown in the Publication example below), but any of the other methods provided by the Comet interface can also be called. See theimport com.priint.pubserver.comet.renderer.Comet
interface in the PubServerSDK for details. - if a technical error occurs, a
com.werkii.server.exception.WorflowJobTechnicalFailureException
should be thrown, on business errors throw acom.werkii.server.exception.WorkflowJobBussinessException
- it is not required to return a specific value - unless you need this for further processing in your Plugin implementation. In the default implementation, the return value is just ignored.
Publication Jobs Example
Publication jobs can be executed during publication workflow processing or be used to extend the functionality of the Publication Planner Download dialog.
Implementation of a PublicationJob requires
- a Job Plugin with a PubServerMethod with MethodType=PUBLICATION_WORKFLOW_JOB and / or MethodType=PUB_PLANNER_DOWNLOAD
- a PublicationJob implementation and
- (optionally) a cscript, which is executed by the renderer.
The example shown below is also part of the DemoDocumentJob project available on the documentation site.
Plugin Implementation
Again, we start with the PubServer Plugin for our new PublicationJob.
As for DocumentJobs, the Plugin serves as a connector for UI integration and the job scheduling engine. Dependeing on the purpose, we just have to implement the PluginControl interface and in addition, if supposed to be available in the Download Dialog, the com.werkii.server.ejb.download.PubPlannerDownloadLocal interface.
For ease of implementation, we can also use the default implementation AbstractPublicationJobPlugin defined in CometServer4SDK.
Core Plugin
package com.priint.pubserver.pubplanner.demo.publication; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.xml.bind.JAXBException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.priint.pubserver.comet.config.CometConfigurationLocal; import com.priint.pubserver.comet.entity.planner.CometBookScript; import com.priint.pubserver.comet.renderer.Parameters; import com.priint.pubserver.comet.renderer.PublicationJob; import com.priint.pubserver.comet.renderer.legacy.AbstractPublicationJobPlugin; import com.priint.pubserver.config.PluginConfigDataHandler; import com.priint.pubserver.exception.PubServerException; import com.priint.pubserver.plugin.PluginControlDefault; import com.priint.pubserver.plugin.PluginMethod; import com.priint.pubserver.plugin.PluginUtils; import com.priint.pubserver.plugin.annotation.PubServerMethod; import com.priint.pubserver.plugin.annotation.PubServerMethodParameter; import com.priint.pubserver.plugin.annotation.PubServerPlugin; import com.werkii.server.PubPlannerPlugins; import com.werkii.server.ejb.download.PubPlannerDownloadLocal; import com.werkii.server.entities.results.GuiParameter; import com.werkii.server.entities.results.GuiParameterComboBoxItem; @Stateless(mappedName=BookScriptPlugin.MAPPED_NAME) @LocalBean @PubServerPlugin public class BookScriptPlugin extends AbstractPublicationJobPlugin implements PubPlannerDownloadLocal { public static final String MAPPED_NAME = "com.priint.pubserver.pubplanner.demo.publication.BookScriptPlugin"; @Override public PublicationJob getPublicationJob(Map<String, Object> parameters) { Integer scriptId = Integer.parseInt((String)parameters.get(Parameters.SCRIPTID)); String tmpPath = (String)parameters.get(Parameters.DESTINATION_TMP_PATH); return new BookScriptJob(scriptId, tmpPath); } @PubServerMethod( label = "name", type = PluginMethod.MethodType.GENERIC, description = "plugin method called to obtain a translation of plugin label") public String getMethodName( @PubServerMethodParameter( name = "lang", defaultValue = PubPlannerPlugins.EN, listOfValues = {}) final String lang) { return "Book script"; } @Override @PubServerMethod( label="Run a book script", type=PluginMethod.MethodType.PUBLICATION_WORKFLOW_JOB) public void exec( @PubServerMethodParameter(name="parameters") final Map<String,Object> parameters) throws PubServerException { super.exec(parameters); } }
Remarks
- this implementation is very similar to the SyncPlaceholderJobPlugin implementation shown above, except:
- we inherit from AbstractPublicationJobPlugin rather than AbstractDocumentJobPlugin.
- type of the
exec
method is MethodType=PUBLICATION_WORKFLOW_JOB
- the
exec
method can be omitted, if this job is supposed to be used in the download Dialog only. - the example already contains import statements for extensions shown below
Download Integration
By adding the following method, this Job is available in the Download Dialog also.
Note: to use a Job in the Download Dialog, you have to implement the PubPlannerDownloadLocal
interface. In the example, this is implicitly implemented by inheritance.
@PubServerMethod( label="Run a book script", type=PluginMethod.MethodType.PUB_PLANNER_DOWNLOAD, description="plugin method called to start download job") public byte[] exec( @PubServerMethodParameter(name="parameters") final Map<String,Object> parameters, @PubServerMethodParameter(name="documents") final List<String> documents) throws PubServerException { return super.exec(parameters, documents); }
Remarks
- as for WORKFLOW_JOBS, only one PUBLICATION_WORKFLOW_JOB or PUB_PLANNER_DOWNLOAD method should be implemented per PubServer Plugin.
- the
exec
method is part of the PubPlannerDownloadLocal contract - we use the super class
exec
implementation, which just returns the result of theexecuteJob
method of the PublicationJob.exec
is expected to return- either ZIP data as byte array
- or
null
. In this case, the job plugin must save job output at a certain location and also add an download information file, so the result can be collected by the download job scheduler.
In this Howto, we cover the simple case (i.e. return byte array) only
Custom Parameters
The
getPublicationJob
method shown above evaluates some values from theparameters
Map. Certain values are defined by the execution environment (e.g. the document selection, path for temporary files etc., reference see below), others can be defined by the user in the workflow configuration or the download dialog, if exposed as UI parameters.The following method defines an UI widget, which allows to select a cscript from a drop down menu.
@PubServerMethod( label = "parameters", type = PluginMethod.MethodType.GENERIC, description = "plugin method called to obtain a list of parameters needed in gui") public List<GuiParameter> getParameters( @PubServerMethodParameter(name = "params") final Map<String, Object> params) { List<GuiParameter> result = new ArrayList<>(); // define combo box for Book scripts: String label = "Script"; List<GuiParameterComboBoxItem> scriptCombo = new ArrayList<>(); CometConfigurationLocal configuration = PluginUtils.getPlugin( CometConfigurationLocal.MAPPED_NAME, PluginControlDefault.getSessionId(), CometConfigurationLocal.class); try { List<PluginConfigDataHandler> scriptConfigs = configuration.getItems(CometBookScript.class); for (PluginConfigDataHandler scriptConfig : scriptConfigs) { CometBookScript script = scriptConfig.readConfig().getCustomConfig(CometBookScript.class); GuiParameterComboBoxItem item = new GuiParameterComboBoxItem( script.getLabel(), Integer.toString(script.getId())); scriptCombo.add(item); } } catch (PubServerException | JAXBException e) { LOGGER.warn("Exception while loading script combo values", e); } GuiParameter scriptWidget = GuiParameter.getComboBox( label, Parameters.SCRIPTID, null, scriptCombo); scriptWidget.setMandatory(true); // apply stored value to the dialog input widget: if (params.containsKey(Parameters.SCRIPTID)) { scriptWidget.setValue(params.get(Parameters.SCRIPTID).toString()); } scriptWidget.getTranslations().put(PubPlannerPlugins.EN, label); result.add(scriptWidget); return result; }
Job Implementation
Job implementations represent the business logic for an actual server job. They must implement the
com.priint.pubserver.comet.renderer.PublicationJob
interface, or - as shown below - use default implementations and inherit fromcom.priint.pubserver.comet.renderer.PublicationJobImpl
.package com.priint.pubserver.pubplanner.demo.publication; import java.util.List; import javax.activation.DataHandler; import com.priint.pubserver.comet.bridge.entitydata.Publication; import com.priint.pubserver.comet.renderer.Comet; import com.priint.pubserver.comet.renderer.Options; import com.priint.pubserver.comet.renderer.PublicationJob; import com.priint.pubserver.comet.renderer.PublicationJobImpl; import com.priint.pubserver.comet.renderer.Renderer.Target; import com.priint.pubserver.exception.PubServerException; import com.priint.pubserver.util.Zip; public class BookScriptJob extends PublicationJobImpl implements PublicationJob { private int scriptId = 0; private String tmpPath = null; // constructor will be called from the Job Plugin public BookScriptJob(final int scriptId, final String tmpPath) { this.scriptId = scriptId; this.tmpPath = tmpPath; } @Override public DataHandler executeJob( String publicationId, List<Publication> publications, Comet renderer) throws PubServerException { renderer.execCScript(Target.global(), this.scriptId, "", Options.NO_OPTIONS, ""); String zipFilePath = Zip.zipFolder(this.tmpPath); return Zip.getDataHandler(zipFilePath); } }
What will happen?
The renderer will be called to execute the cscript selected in the Workflow configuration resp. Download Dialog UI.Remarks
- The BookScriptJob constructor is called from the
getPublicationJob
method implemented in the Plugin. As seen above, we evaluate parameters from UI and environment in this method and pass them as arguments to the Job implementation.
There is no way to evaluate parameters in the job implementation directly. While this may seem like a limitation, this on the other hand helps to keep the UI and controlling logic apart from the business logic. -
Note, that we use the same key (
Parameters.SCRIPTID
) in the getParameters and getPublicationJob methods. - The
Parameters.DESTINATION_TMP_PATH
parameter is defined by the RenderingServer and also propagated to the cscript environment as global variable (gDestinationPath). You do not necessarily have to use this predefined temporary path, if required, you can override it like this:Options options = new Options(); try { options.add(Option.DOWNLOAD_TMP_PATH, "/my/preferred/path/"); } catch (ValidationFailedException e) { // exception handling ... } renderer.execCScript(Target.global(), this.scriptId, "", options, ""); // etc.
Script Implementation
Finally, as our job is supposed to execute a cscript, we need a script implementation.
Detailed description of the cscript API can be found in the Plugins documentation, so for this Howto, we limit to the commented script code:
The example script creates a book at the destination path provided by the job implementation, adds all selected documents and finally exports a PDF file.#include "internal/products.h" int main() { Publication tmp; ItemRef book = item::alloc (); char * bookDestinationPath = alloc (4096); char * publicationPath = alloc (4096); int i = 0; int error = 0; strcpy (bookDestinationPath, gDestinationPath); strcat (bookDestinationPath, "/book.indb"); error = book::create (bookDestinationPath); if (error != 0) { // ... error handling } error = book::open (bookDestinationPath, book); for (i = 0; i < publication::publicationlist::length(gPublications); ++i) { tmp = publication::publicationlist::get (gPublications, i); strcpy (publicationPath, publication::gets (tmp, kPath)); // IMPORTANT! call insert with repaginate=0, otherwise each insert // call will trigger repagination of ALL documents added by now: book::insert (book, publicationPath, -1, 0, 0, 0); } // AFTER adding all documents call repaginate book::repaginate (book); // save and close book error = book::save (book); error = book::export_ (book, 0); error = book::close (book); release (bookDestinationPath); release (publicationPath); return error; }
One remark though
In this script, we use several global variables, which are only defined in the Server environemnt, namelygPublications
(PublicationList, all documents, which are allowed to be opened / used in this script) andgDestinationPath
(path for output files), we cannot directly call this script e.g. in InDesign Desktop.Though defined for all cscripts in the Server environment, for most situations these variables are empty.
To keep publication scripts, which use these variables, apart from scripts, which do not use / require them, the new script type
CometBookScript
has been introduced. Scripts of this type can be added in ISON in the "Management Planner > Books" node.Environment
The parameters map can contain arbitrary parameters for this job, including user defined / project specific parameters. Though, some parameters are set by the execution environment and are always available. These are set by the job scheduler, which starts the Document- resp. PublicationJob.
See the SDK documentation for details, especially regarding methods required for UI parameters and display name.
Common DocumentJob Parameters
- Parameters.DOCUMENTID: the ID of the document being processed.
- Parameters.SCRIPTID: the ID of the script to be executed (if this job executes a script).
Common PublicationJob Parameters
- Parameters.DOCUMENTS: list of document IDs being processed in this job.
- Parameters.PUBLICATIONS: list of publication IDs being processed in this job.
- Parameters.SCRIPTID: the ID of the script to be executed (if this job executes a script).
- Parameters.SELECTION_LIST: list of all selections (documents and publications) being processed in this job.
- Parameters.DESTINATION_TMP_PATH: path for output files. This is the contract between PublishingServer and InDesignServer regarding expected output files
- Parameters.DOWNLOAD_TMP_PATH: path to download directory.
Overriding Parameters
Most of the parameters listed above should not be overriden, though using a different destination path than the one defined by the job scheduler is a common requirement.
The right time or the right place to set or override parameters is the Job Plugin exec method before calling the super.exec() method. From the job sheduler point of view, this i s the "entry point" of job execution, all job processing is done after this.
Parameters.DESTINATION_TMP_PATH resp. gDestinationPath is the contract between PublishingServer and InDesignServer regarding expected output files. These values must match and denote a valid path, which can be resolved by PubServer and InDesignServer.
By setting this value at the right time, we make sure, that it is propagated to all affected components (in the example case BookScriptJob, InDesignServer / cscript).Example Overriding gDestinationPath
@PubServerMethod( label="PDF Download", type=PluginMethod.MethodType.PUB_PLANNER_DOWNLOAD, description="plugin method called to start download job") public byte[] exec( @PubServerMethodParameter(name="parameters") final Map<String,Object> parameters, @PubServerMethodParameter(name="documents") final List<String> documents) throws PubServerException { // in the following line, we override parameters set by the execution environment: parameters.put(Parameters.DESTINATION_TMP_PATH, "C:\\customized-destinationpath\\"); return super.exec(parameters, documents); }
Renderer Environment
When cscripts are executed in a PublicationJob environment, some additional environment variables are defined for this script:- gPublications (PublicationList): all documents, which are allowed to be processed in this script. The documents are not opened, but may be opened and closed in the script.
Corresponds to the Java Parameters.DOCUMENTS parameter - gPublicationId (char *): the ID of the publication, which is being processed. If several publications are selected, this is the ID of the first publication in the selection. If no publication is selected (only documents), the ID is empty.
Corresponds to the first element (if available) of the Java Parameters.PUBLICATIONS parameter - gDestinationPath (char *): if the script generates any output, it must be saved to gDestinationPath (this is, where the Application server expects generated files such as PDFs etc.).
Corresponds to the Java Parameters.DESTINATION_TMP_PATH parameter