LiveCycle Content Services (deprecated) provides you with
the ability to develop custom actions. A custom action can be used
to invoke a LiveCycle service or process. This section focuses
on the task of invoking a LiveCycle service from a custom
action. (See
Creating Custom Actions
.)
Note:
Adobe is migrating Content Services ES customers to the Content Repository built on the modern, modular CRX architecture, acquired during the Adobe acquisition of Day Software. The Content Repository is provided with LiveCycle Foundation and is available as of the LiveCycle ES4 release.
To understand how custom actions can be used to invoke a LiveCycle service, consider the following scenario, which will be
used as the basis for all code examples in this section.
Assume that you want to create a custom action that invokes a
review and approval process created in workbench.
The content model requires that you define a
reviewer
aspect
that is available in the Content Services (deprecated) content model so that
it can be applied by using the custom action in the repository.
a LiveCycle invocation class is needed to encapsulate
the business logic to invoke the process from the custom action.
After you create the logic , you must package and deploy your
custom action to Content Services (deprecated).
The review and approval example discussed in this section shows
how to create a custom action that invokes the LiveCycle process, and is based on a sample located within your SDK installation
at \sdk\misc\ContentServices\adobe-contentservices-src.zip\process-samples.
Start Advanced Process vs. Invoke a Process
This
chapter dicusses how to create an "Advanced Process" to be invoked through
the Content Space. It is important to understand the difference
between "Invoke a LiveCycle Process" available from the "Run Action"
menu and the "Start Advanced Process" available from the "Details"
menu.
The "Invoke a LiveCycle Process" can be used with any
process that has one input variable of type document. Any other
combination of inputs will cause the process to not be listed in
the process drop-down box. When you choose the "Invoke a LiveCycle
Process" action, the selected the node(document) in the Content
Space will be used as an input to the selected process. The "Advanced Process",
discussed in this chapter, is useful in situations where multiple
inputs are required for the process to be invoked. The user is prompted
to enter values with a wizard-like interface. Once all the inputs
have been filled, the process is invoked. The "Advanced Process"
is useful for invoking long-lived processes and complex workflows.
Summary of steps
To develop a custom action, perform the following
steps:
-
Set up your development environment.
-
Define your application logic.
-
Define the user interface resources.
-
Package and deploy the component.
Setting up your development environment
To
create a custom action, you must set the Eclipse Compiler Compliance
Level to Java SE Development Kit version 1.5 (5.0) or later, and
then you must import the JAR files located in \lib\server folder
located in the SDK zip file, including the following JAR files:
-
alfresco-repository.jar
-
alfresco-core.jar
-
alfresco-web-client.jar
-
alfresco-webscript-framework.jar
You must also include
the following JAR files in the build path:
-
myfaces-api-1.1.5.jar located in the \dependencies\ folder
-
spring-2.0.8.jar located in the \dependencies\ folder
-
jta.jar located in the \dependencies\ folder
-
servlet.jar located in the \dependencies\devenv\ folder
-
adobe-usermanager-client.jar
-
adobe-livecycle-client.jar
Finally, you must also include
all the JAR files within \sdk\misc\ContentServices\adobe-contentservices-src.zip\ContentServices\lib\
Defining your application logic
Creating a custom model
To define a custom model, in some cases, create a custom
model to contain the new aspect and tags to be assigned by the user.
In this example, the model uses a prefix of
lcProcess
, the
aspect is called
lcProcess:reviewer
, and the property is called
lcProcess:reviewDescription
.
This example contains a sample implementation of the review and
approval custom model and is defined in the liveCycleProcessModel.xml
file.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Definition of new Model -->
<model name="lcProcess:model" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<!--Optional metadata about the model -->
<description>LiveCycle Process Model</description>
<author>LiveCycle Content Service ES</author>
<version>1.0</version>
<!--Imports are required to allow references to definitions in other models-->
<imports>
<!--Import Alfresco Dictionary Definitions -->
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
<!--Import Alfresco Content Domain Model Definitions -->
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
</imports>
<!--Introduction of new namespaces defined by this model -->
<namespaces>
<namespace uri="http://www.adobe.com/lc/process/1.0" prefix="lcProcess"/>
</namespaces>
<types>
<type name="lcProcess:reviewAndApproval">
<parent>cm:content</parent>
<properties>
<!-- Task Description -->
<property name="lcProcess:reviewDescription">
<title>Review Description</title>
<type>d:text</type>
</property>
</properties>
<mandatory-aspects>
<aspect>lcProcess:reviewer</aspect>
</mandatory-aspects>
</type>
</types>
<aspects>
<!-- -->
<!-- Task Reviewer -->
<!-- -->
<aspect name="lcProcess:reviewer">
<associations>
<association name="lcProcess:reviewer">
<title>Reviewer</title>
<source>
<mandatory>false</mandatory>
<many>false</many>
</source>
<target>
<class>cm:person</class>
<mandatory>true</mandatory>
<many>false</many>
</target>
</association>
</associations>
</aspect>
</aspects>
</model>
Implementing the action executor class
The custom action consists of an
ActionExecutor
class
and its associated bean declaration. An action must implement the
org.alfresco.repo.action.executor.ActionExecuter
interface. The
ActionExecuter
interface
is best implemented by extending the abstract class
ActionExecuterAbstractBase
,
which provides basic services for action executor implementations.
Your implementation of the
ActionExecuterAbstractBase
interface must
include an
addParameterDefinitions
method, which
is used to add parameters to the custom repository action, as well
as an
executeImpl
method, which contains the actual
business logic.
The name of the action executor is defined by the static
NAME
attribute,
which is assigned the value of
"process-action"
as
shown in the following code example.
public class ReviewProcessActionExecuter extends ActionExecuterAbstractBase
{
// process name - this will be used to refer to the action.
public static final String NAME = "process-action";
Adding action parameters
To implement the
addParameterDefinitions
method,
go into its definition and create a new
ParameterDefinition
object,
as shown in the following code example. This example shows how to
use the parameter list to add a new parameter definition. In this
example,
PARAM_REVIEWER_ID
,
PARAM_ASSIGNER_ID
,
and
PARAM_REVIEW_DESC
contain the names of the parameters.
// action parameters
// reviewer name
public static final String PARAM_REVIEWER_ID = "reviewer-id";
// assigner name
public static final String PARAM_ASSIGNER_ID = "assigner-id";
// review description
public static final String PARAM_REVIEW_DESC = "review-desc";
protected void addParameterDefinitions(List<ParameterDefinition> paramList) {
paramList.add(new ParameterDefinitionImpl(PARAM_REVIEWER_ID, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_REVIEWER_ID)));
paramList.add(new ParameterDefinitionImpl(PARAM_ASSIGNER_ID, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_ASSIGNER_ID)));
paramList.add(new ParameterDefinitionImpl(PARAM_REVIEW_DESC, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_REVIEW_DESC)));
}
Implementing the custom action
The
executeImpl
method
is where the custom action business logic resides. It receives an
Action
object
and the
NodeRef
of the node on which the action
is performed. In this example, the
executeImpl
method
adds the
reviewer
aspect to the node and uses the
list to set the node’s
reviewDescription
property.
Because
the
executeImpl
method uses
NodeService
,
PersonService
,
AuthenticationService
,
and
UserManagerUtils
, you must inject those services
by using Spring dependency injection, as shown in the following code
example.
The following example shows how to inject services.
private NodeService nodeService;
private AuthenticationService authenticationService;
private PersonService personService;
private UserManagerUtils userManagerUtils = null;
// Inject Node Service.
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
// Inject Person Service.
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
// Inject Authentication Service.
public void setAuthenticationService(AuthenticationService aAuthenticationService)
{
this.authenticationService = aAuthenticationService;
}
// Inject UserManagerUtils
public void setUserManagerUtils(UserManagerUtils aUserManagerUtils)
{
this.userManagerUtils = aUserManagerUtils;
}
In this case, the custom action invokes a LiveCycle process, so the
ReviewProcessActionExecuter.executeImpl
method
contains logic to obtain the user context, create a service client
factory, and invoke the LiveCycle process. To obtain the
user context, invoke the
AuthenticationService.getCurrentTicket
method
to obtain a ticket, and pass that ticket to the
UserManagerUtils.getIDPContext
method.
To create the service client factory, pass the user context to the
ServiceClientFactory.createInstance
method,
as shown in the following example.
Obtaining the user context
This example shows how to obtain the
user context and create a service client. For information about
creating service clients, see
Invoking a service using a Java client library
.
String ticket = authenticationService.getCurrentTicket();
Context idpContext = userManagerUtils.getIDPContext(ticket);
ServiceClientFactory _scFactory = ServiceClientFactory.createInstance(idpContext);
For
this example, the class defining the methods in which LiveCycle services
are invoked is called
LiveCycleServiceInvoker
.
The
ReviewProcessActionExecuter.executeImpl
method
invokes the process by using the methods in the
LiveCycleServiceInvoker
class,
as shown in the following example.
Invoking the LiveCycle process
This example contains the
logic that is contained within the
ReviewProcessActionExecuter.executeImpl
method
to create an instance of the
LiveCycleInvoker
class,
set its client factory, and use the class to invoke a LiveCycle
process.
LiveCycleServiceInvoker _lcServiceInvoker = new LiveCycleServiceInvoker();
_lcServiceInvoker.setServiceClientFactory(_scFactory);
try {
_lcServiceInvoker.invokeLongLivedOrchestration(NAME_REVIEW_AND_APPROVAL_PROCESS, _processParamMap);
} catch (DSCException e) {
e.printStackTrace();
throw new ProcessExecutionException(e.getMessage());
}
Defining the ReviewProcessActionExecuter implementation
This example
shows the entire implementation of the
ReviewProcessActionExecuter
class,
in which the business logic is contained in the
executeImp
method,
and the parameters are added through the
addParameterDefinitions
method.
package com.adobe.livecycle.samples.cs.action;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import com.adobe.contentservices.authentication.UserManagerUtils;
import com.adobe.contentservices.common.ContentServicesConstants.CSContentModel;
import com.adobe.contentservices.logging.CSModulesLogger;
import com.adobe.idp.Context;
import com.adobe.idp.dsc.DSCException;
import com.adobe.idp.dsc.clientsdk.ServiceClientFactory;
import com.adobe.livecycle.samples.cs.util.LiveCycleServiceInvoker;
/**
* This is an Action Executer class to define an action (invocable from Java Script) that
* can be used to initiate a Review and Approval process. It can be used to execute a Review
* and Approval action from the Office Plug-in.
*
* @author Akhil Kumar Jain
*/
public class ReviewProcessActionExecuter extends ActionExecuterAbstractBase
{
// specify logger
private static final CSModulesLogger s_csLogger = CSModulesLogger.getCSModuleLogger(ReviewProcessActionExecuter.class);
// process name - this will be used to refer to the action.
public static final String NAME = "process-action";
// action parameters
// reviewer name
public static final String PARAM_REVIEWER_ID = "reviewer-id";
// assigner name
public static final String PARAM_ASSIGNER_ID = "assigner-id";
// review description
public static final String PARAM_REVIEW_DESC = "review-desc";
// LiveCycle service that will be invoked for this action.
private static final String NAME_REVIEW_AND_APPROVAL_PROCESS = "Samples - Content Services (deprecated) - ReviewAndApproval";
// parameters for the "Samples - Content Services (deprecated) - ReviewAndApproval" service
private static final String PARAMETER_IN_REVIEWER_OID = "inReviewerOID";
private static final String PARMATER_IN_REVIEW_DESCRIPTION = "inReviewDescription";
private static final String PARAMETER_IN_REVIEW_INITIATOR_OID = "inReviewInitiatorOID";
private static final String PARAMETER_IN_STORE_NAME = "inStoreName";
private static final String PARAMETER_IN_REVIEW_CONTENT_URLLIST = "inReviewContentURLList";
private NodeService nodeService;
private AuthenticationService authenticationService;
private PersonService personService;
private UserManagerUtils userManagerUtils = null;
// Inject Node Service.
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
// Inject Person Service.
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
// Inject Authentication Service.
public void setAuthenticationService(AuthenticationService aAuthenticationService)
{
this.authenticationService = aAuthenticationService;
}
// Inject UserManagerUtils
public void setUserManagerUtils(UserManagerUtils aUserManagerUtils)
{
this.userManagerUtils = aUserManagerUtils;
}
/**
* This method is used to specify the parameters for the action.
* @param The list that should be filled with all the parameters.
*/
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
paramList.add(new ParameterDefinitionImpl(PARAM_REVIEWER_ID, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_REVIEWER_ID)));
paramList.add(new ParameterDefinitionImpl(PARAM_ASSIGNER_ID, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_ASSIGNER_ID)));
paramList.add(new ParameterDefinitionImpl(PARAM_REVIEW_DESC, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_REVIEW_DESC)));
}
/**
* This method is used to define the flow when the action defined by this ActionExecuter is
* executed.
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef)
*/
protected void executeImpl(
Action ruleAction,
NodeRef actionedUponNodeRef)
{
s_csLogger.info("Invoking action ==> " + ReviewProcessActionExecuter.NAME, "");
if (this.nodeService.exists(actionedUponNodeRef) == true)
{
// Get the parameter values
String assigner = (String)ruleAction.getParameterValue(PARAM_ASSIGNER_ID);
String reviewer = (String)ruleAction.getParameterValue(PARAM_REVIEWER_ID);
String reviewDesc = (String)ruleAction.getParameterValue(PARAM_REVIEW_DESC);
s_csLogger.info("Fetching action parameters ..." , "");
// Step: Collect the process parameters
java.util.Map<String, Object> _processParamMap = new java.util.HashMap<String, Object>();
// Get the Review Initiator OID
String _reviewInitiatorOID = getUserOID(assigner);
s_csLogger.debug("ReviewerInitiatorName=" + assigner + ",OID=" + _reviewInitiatorOID, "");
_processParamMap.put(PARAMETER_IN_REVIEW_INITIATOR_OID, _reviewInitiatorOID);
// Get the Review Description
s_csLogger.debug("ReviewDescription=" + reviewDesc);
_processParamMap.put(PARMATER_IN_REVIEW_DESCRIPTION, reviewDesc);
// Get the Reviewer OID
s_csLogger.debug("ReviewerName=" + reviewer + ",OID=" + getUserOID(reviewer), "");
_processParamMap.put(PARAMETER_IN_REVIEWER_OID, getUserOID(reviewer));
String nodeName = actionedUponNodeRef.toString();
// Get the Store name
// Items are of the form: workspace://SpacesStore/bb090e72-bf94-11dc-af8e-7d7d65ccd02d
String[] _itemWebURLSplit = nodeName.split("//");
String _itemURL = _itemWebURLSplit[1];
String[] _itemURLSplit = _itemURL.split("/");
_processParamMap.put(PARAMETER_IN_STORE_NAME, _itemURLSplit[0]);
// Construct Review Content URL List
java.util.List<String> _reviewContentURLList = new ArrayList<String>();
int _idx = nodeName.lastIndexOf("/");
if (_idx == -1 || ((_idx+1) == nodeName.length()))
{
throw new IllegalArgumentException("Illegal URL [" + nodeName + "]");
}
_reviewContentURLList.add(nodeName.substring(_idx+1));
s_csLogger.debug("Content URL List=" + _reviewContentURLList, "");
_processParamMap.put(PARAMETER_IN_REVIEW_CONTENT_URLLIST, _reviewContentURLList);
// Step: Invoke "Samples - Content Services (deprecated) - ReviewAndApproval" service
// Get the User Context
String ticket = authenticationService.getCurrentTicket();
Context idpContext = userManagerUtils.getIDPContext(ticket);
ServiceClientFactory _scFactory = ServiceClientFactory.createInstance(idpContext);
s_csLogger.info("Invoking Service " + NAME_REVIEW_AND_APPROVAL_PROCESS, "");
// Invoke the process with the given parameters
LiveCycleServiceInvoker _lcServiceInvoker = new LiveCycleServiceInvoker();
_lcServiceInvoker.setServiceClientFactory(_scFactory);
try {
_lcServiceInvoker.invokeLongLivedOrchestration(NAME_REVIEW_AND_APPROVAL_PROCESS, _processParamMap);
} catch (DSCException e) {
e.printStackTrace();
throw new ProcessExecutionException(e.getMessage());
}
s_csLogger.info("Process: '" + NAME_REVIEW_AND_APPROVAL_PROCESS + "' launch COMPLETED.", "");
}
}
/** Gets the OID for the given user */
private String getUserOID(String userName)
{
NodeRef _personNode = personService.getPerson(userName);
return (String)nodeService.getProperty(_personNode, QName.createQName(CSContentModel.PROP_OID));
}
}
Implementing the invoker class
To invoke a LiveCycle service, use the service client factory
to create an invocation request and pass that request to the service
client’s
invoke
method. (See
Invoking a service using a Java client library
.)
Defining the LiveCycle service invocation class
This example
shows the implementation of the
LiveCycleServiceInvoker
class
that contains the application logic for creating a service client
and invoking the service or process, which is used by the
ReviewProcessActionExecuter.executeImpl
method.
The primary logic in which the invocation request is created is
in the
invokeOperation
method shown in this example.
/**
* ADOBE SYSTEMS INCORPORATED
* Copyright 2008 Adobe Systems Incorporated
* All Rights Reserved
*
* NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the
* terms of the Adobe license agreement accompanying it. If you have received this file from a
* source other than Adobe, then your use, modification, or distribution of it requires the prior
* written permission of Adobe.
*/
package com.adobe.livecycle.samples.cs.util;
import com.adobe.contentservices.logging.CSModulesLogger;
import com.adobe.idp.dsc.DSCException;
import com.adobe.idp.dsc.InvocationRequest;
import com.adobe.idp.dsc.InvocationResponse;
import com.adobe.idp.dsc.clientsdk.ServiceClientFactory;
/**
* LiveCycle Service Invoker
*
* @author Rohit Kumar (rohitk@adobe.com)
* @since 24 December 2007
*/
public class LiveCycleServiceInvoker
{
private static final CSModulesLogger s_logger = CSModulesLogger.getCSModuleLogger(LiveCycleServiceInvoker.class);
private static final String ORCHESTRATION_OPERATION_NAME = "invoke";
private ServiceClientFactory m_serviceClientFactory = null;
/** Setter the Service Client Factory */
public void setServiceClientFactory(ServiceClientFactory aServiceClientFactory)
{
m_serviceClientFactory = aServiceClientFactory;
}
/** Invokes a Long Lived Orchestration with the give parameters */
public void invokeLongLivedOrchestration(String serviceName, java.util.Map<String, Object> paramMap)
throws DSCException
{
s_logger.debug("Invoking Long Lived Orchestration [" + serviceName + "]");
s_logger.debug("Service Parameters=" + paramMap);
invokeOperation(serviceName, ORCHESTRATION_OPERATION_NAME, paramMap, false);
}
/** Invokes a Short Lived Orchestration with the give parameters */
public java.util.Map invokeShortLivedOrchestration(String serviceName, java.util.Map<String, Object> paramMap)
throws DSCException
{
return invokeOperation(serviceName, ORCHESTRATION_OPERATION_NAME, paramMap, true);
}
/** Executes aServiceName.aOperationName(operationParams) */
public java.util.Map invokeOperation(String aServiceName, String aOperationName,
java.util.Map<String, Object> operationParamMap, boolean isSynchronousInvocation)
throws DSCException
{
InvocationRequest _request = m_serviceClientFactory.createInvocationRequest(
aServiceName, aOperationName, operationParamMap, isSynchronousInvocation);
InvocationResponse _response = m_serviceClientFactory.getServiceClient().invoke(_request);
return _response.getOutputParameters();
}
}
Defining the user interface resources
The user interface resources may be defined through the
action and parameter messages, the action JavaServer page (JSP),
an action handler, a Spring configuration file that is used to register
the action as a bean, and a configuration file. (See
Creating Custom Actions
.)
Packaging and deploying the custom action component
|
|
|