You can call the Correspondence Management solution APIs
from within the LiveCycle platform (process/orchestration) as part
of a larger workflow.
To build a custom service container, the following libraries
are required: com.springsource.org.aopalliance-1.0.0.jar
spring-aop-3.0.2.RELEASE.jar
spring-asm-3.0.2.RELEASE.jar
spring-beans-3.0.2.RELEASE.jar
spring-context-3.0.2.RELEASE.jar
spring-core-3.0.2.RELEASE.jar
spring-expression-3.0.2.RELEASE.jar
spring-security-core-3.0.2.RELEASE.jar
spring-security-web-3.0.2.RELEASE.jar
spring-web-3.0.2.RELEASE.jar
dom4j-1.6.1.jar
commons-logging-1.1.jar
adobe-acm-client.jar
adobe-dct-client.jar
lc-content-api.jar
Implementation OverviewThe examples below create custom service container that
either expose (service container operations within your orchestration):
Custom Service as a service container operationYou can implement a custom service operation that uses
the Correspondence Management APIs to implement some functionality,
and then expose this operation within your service container container.
To do so, perform the following tasks: Create your custom
service
Register the service as a bean
Create the Externalized Property file for the Component
Export the custom service as a LiveCycle Service
Define the service container Component
Create your custom serviceWrite an interface-based custom service that uses the exposed
Correspondence Management APIs to perform the required custom task.
The following is an example that exposes a service to retrieve the
processed Layout template of a correspondence, and the processed
XML data, so that they can be merged to produce the correspondence
in a custom format :
The interface, IRenderServiceWrapper.java::
package com.adobe.livecycle.cmsa;
import java.util.Map;
import com.adobe.idp.Document;
public interface IRenderServiceWrapper {
public abstract Map<String, Document> getProcessedTemplate(String letterName, String initialXmlData, Boolean useTestData);
}
The implementation, RenderServiceWrapper.java:
package com.adobe.livecycle.cmsa;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.adobe.icc.dbforms.obj.Letter;
import com.adobe.icc.dbforms.obj.Query;
import com.adobe.icc.dbforms.obj.Statement;
import com.adobe.icc.dbforms.obj.Statement.Operator;
import com.adobe.icc.ddg.api.LetterRenderService;
import com.adobe.icc.services.api.LetterService;
import com.adobe.idp.Document;
public class RenderServiceWrapper implements IRenderServiceWrapper {
private LetterRenderService renderService;
private LetterService letterService;
/**
* {@inheritDoc}
*/
public Map<String, Document> getProcessedTemplate(String letterName, String initialXmlData, Boolean useTestData)
{
Map<String, Document> result = new HashMap<String, Document>();
if (letterName != null && !"".equals(letterName))
{
Statement st = new Statement();
st.setAttributeName("name");
st.setOperator(Operator.EQUALS);
st.setAttributeValue(letterName);
Query query = new Query();
query.setObjectType(Letter.class.getSimpleName());
query.addStatement(st);
List<Letter> letters = letterService.getAllLetters(query);
if (letters.size() > 0)
{
Map<String, Object> processedData = renderService.processLetter(letters.get(0).getId(), initialXmlData, useTestData);
Document xdp = new Document((byte[])processedData.get(LetterRenderService.LAYOUT_TEMPLATE_KEY));
Document xml = new Document((byte[])processedData.get(LetterRenderService.XML_DATA_KEY));
result.put(LetterRenderService.LAYOUT_TEMPLATE_KEY, xdp);
result.put(LetterRenderService.XML_DATA_KEY, xml);
}
else
System.out.println("No Letter found with name : " + letterName);
}
else
System.out.println("No Letter name provided.");
return result;
}
public LetterRenderService getRenderService() {
return renderService;
}
public void setRenderService(LetterRenderService renderService) {
this.renderService = renderService;
}
public LetterService getLetterService() {
return letterService;
}
public void setLetterService(LetterService letterService) {
this.letterService = letterService;
}
}
Register as a Spring beanOnce the above service is implemented, register it as a
Spring bean within your (Spring-based) custom service container:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="lc.remotingClientParent"
class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"
abstract="true">
<property name="httpInvokerRequestExecutor" ref="CRXAuthHttpInvokerRequestExecutor" />
</bean>
<bean id="CRXAuthHttpInvokerRequestExecutor"
class="com.adobe.livecycle.spring.remoting.CRXAuthHttpInvokerRequestExecutor">
<property name="appRoot" value="/content/apps/cm"/>
</bean>
<bean name="LetterService" parent="lc.remotingClientParent">
<property name="serviceUrl"
value="${crx.serverUrl}/bin/remoting/lc.icc.dbservices.letterService" />
<property name="serviceInterface" value="com.adobe.icc.services.api.LetterService" />
</bean>
<bean name="LetterRenderService" parent="lc.remotingClientParent">
<property name="serviceUrl"
value="${crx.serverUrl}/bin/remoting/lc.icc.renderlib.letterRenderService" />
<property name="serviceInterface" value="com.adobe.icc.ddg.api.LetterRenderService" />
</bean>
<bean name="RenderServiceWrapper" class="com.adobe.livecycle.cmsa.RenderServiceWrapper">
<property name="renderService" ref="LetterRenderService" />
<property name="letterService" ref="LetterService" />
</bean>
<bean id="testProps"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:cmsa.properties</value>
<value>file:C:/Adobe/config/cmsa.properties</value>
</list>
</property>
</bean>
</beans>
Create the Externalized Property file for the ComponentThe following is an example of the default property file
(cmsa.properties) within the component in the classpath: # URL to the Spring app.
crx.serverUrl=http://localhost:4502
# more propeties here>
Export the custom service as a LiveCycle serviceNow that the services are available as a Spring bean, you
can export them as a LiveCycle service. Define a com.adobe.idp.dsc.component.ServiceInstanceFactory implementation
(SpringServiceFactory.java) as follows:
package com.adobe.livecycle.spring;
import com.adobe.idp.dsc.DSCException;
import com.adobe.idp.dsc.component.ComponentContext;
import com.adobe.idp.dsc.component.support.AbstractServiceInstanceFactory;
import com.adobe.idp.dsc.registry.infomodel.ServiceConfiguration;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
public class SpringServiceFactory extends AbstractServiceInstanceFactory {
public void activateInstance(ServiceConfiguration serviceConfiguration,
Object o) {
}
public Object createInstance(ServiceConfiguration serviceConfiguration)
throws DSCException {
return getBeanFactory(serviceConfiguration).getBean(
serviceConfiguration.getServiceId());
}
public void destroyInstance(ServiceConfiguration serviceConfiguration,
Object o) throws DSCException {
}
public void passivateInstance(ServiceConfiguration serviceConfiguration,
Object o) {
}
public boolean validateInstance(ServiceConfiguration serviceConfiguration,
Object o) {
return true;
}
private BeanFactory getBeanFactory(ServiceConfiguration serviceConfiguration) {
String componentId = serviceConfiguration.getComponentId();
ComponentContext ctx = getComponentContext();
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(ctx.getClassLoader());
return ContextSingletonBeanFactoryLocator.getInstance(
"classpath:/spring/beanRefContext.xml").useBeanFactory(
componentId).getFactory();
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
}
The factory in the example above expects a Spring configuration
file named beanRefContext.xml under classpath:/spring,
which loads the actual client’s Spring configuration/beans. For
example:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
xmlns:context="http://www.springframework.org/schema/context">
<!--
Note: The beanId must be same as the componentId
-->
<bean name="com.adobe.livecycle.cmsa.RenderWrapper" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<property name="configLocations">
<value>
classpath:cmsa-spring-config.xml
</value>
</property>
</bean>
</beans>
Follow these conventions to simplify the component/service definitions
and expose the beans as a LiveCycle service:
The bean ID or name in beanRefContext.xml is the
same as the component ID, as defined in the service container's
component.xml.
The custom service bean has the same name as the LiveCycle
service ID, as defined in the service container's component.xml.
Define the service container ComponentNext, define the service container component details in
its component.xml as follows:
<component xmlns="http://adobe.com/idp/dsc/component/document">
<component-id>com.adobe.livecycle.cmsa.RenderWrapper</component-id>
<version>1.0</version>
<services>
<!-- Service name must be same as the name of the corresponding Spring
bean -->
<service name="RenderServiceWrapper" orchestrateable="true">
<implementation-class>com.adobe.livecycle.cmsa.IRenderServiceWrapper
</implementation-class>
<auto-deploy service-id="RenderServiceWrapper"
minor-version="0" major-version="1" category-id="Correspondence Management" />
<operations>
<operation name="getProcessedTemplate">
<input-parameter name="letterName" title="Letter Name"
type="java.lang.String" />
<input-parameter name="initialXmlData" title="Initial XML Data (for DDI)"
type="java.lang.String" />
<input-parameter name="useTestData" title="Use Letter Test Data?"
type="java.lang.Boolean" />
<output-parameter name="result"
title="Result Map with Layout and Data" type="java.util.Map" />
</operation>
</operations>
</service>
<!-- Mandatory service to capture CRX credentials -->
<service name="CRXUserCredentialService">
<implementation-class>com.adobe.livecycle.crx.CRXUserCredentialService
</implementation-class>
<factory-method>getInstance</factory-method>
<supported-connectors></supported-connectors>
<auto-deploy major-version="1" service-id="CRXUserCredentialService"
category-id="Correspondence Management" />
<config-parameter name="username" type="java.lang.String"
title="Username">
<description>User name with which to connect to CM</description>
<default-value>admin</default-value>
</config-parameter>
<config-parameter name="password" type="java.lang.String"
title="Password">
<description>Password for the above user</description>
<default-value>admin</default-value>
<property-editor editor-id="com.adobe.idp.dsc.propertyeditor.system.PasswordPropertyEditorComponent" />
</config-parameter>
<operation-config>
<operation-name>*</operation-name>
<transaction-type>None</transaction-type>
</operation-config>
</service>
</services>
<supports-export>false</supports-export>
<class-path>lib/dom4j-1.6.1.jar lib/lc-content-api.jar lib/adobe-acm-client.jar lib/adobe-dct-client.jar lib/com.springsource.org.aopalliance-1.0.0.jar lib/spring-aop-3.0.2.RELEASE.jar lib/spring-asm-3.0.2.RELEASE.jar lib/spring-beans-3.0.2.RELEASE.jar lib/spring-context-3.0.2.RELEASE.jar lib/spring-core-3.0.2.RELEASE.jar lib/spring-expression-3.0.2.RELEASE.jar lib/spring-web-3.0.2.RELEASE.jar lib/spring-security-core-3.0.2.RELEASE.jar</class-path>
<service-factory-class>com.adobe.livecycle.spring.SpringServiceFactory
</service-factory-class>
</component>
Exposing Correspondence Management services directly as a service container operationYou can also expose the entire Correspondence Management
service, that is already exposed over HTTP remoting, and its operations
as a LiveCycle service, rather than wrapping it within a custom
service. For example, to expose the FormService and its operations,
simply add the following entries to the component.xml:
<component xmlns="http://adobe.com/idp/dsc/component/document">
...
...
<services>
<!-- Again, the Service name/id must be same as the name of the corresponding Spring bean, defined in cmsa-spring-config.xml -->
<service name="FormService" orchestrateable="true">
<implementation-class>com.adobe.livecycle.cmsa.FormQueryService</implementation-class>
<auto-deploy service-id="FormService" minor-version="0" major-version="1" category-id="CMSA"/>
</service>
...
<!-- more services here -->
...
</services>
...
...
</component>
Authentication propagationYou can establish an invocation context for CRX so that
the service container configuration can be propagated to call Correspondence
Management APIs. Create a custom service configuration that captures
a set of CRX user credentials that have permissions over Correspondence
Management content. The following definition exposes the configurations
on the LiveCycle Doc Services Platform Admin user interface.:
<service name="CRXUserCredentialService">
<implementation-class>com.adobe.livecycle.crx.CRXUserCredentialService
</implementation-class>
<factory-method>getInstance</factory-method>
<supported-connectors></supported-connectors>
<auto-deploy major-version="1" service-id="CRXUserCredentialService"
category-id="Correspondence Management" />
<config-parameter name="username" type="java.lang.String"
title="Username">
<description>User name with which to connect to CM</description>
<default-value>admin</default-value>
</config-parameter>
<config-parameter name="password" type="java.lang.String"
title="Password">
<description>Password for the above user</description>
<default-value>admin</default-value>
<property-editor editor-id="com.adobe.idp.dsc.propertyeditor.system.PasswordPropertyEditorComponent" />
</config-parameter>
<operation-config>
<operation-name>*</operation-name>
<transaction-type>None</transaction-type>
</operation-config>
</service>
To configure: Navigate to Admin user interface > Services > Applications and Services > Service Management.
Select Correspondence Management as the Category.
Select the CRXUserCredentialService service.
Specify the user name and password, that you created for
your CRX instance, that has access to Correspondence Management.
Click Save.
Custom authentication executorThe following CRXAuthHttpInvokerRequestExecutor.java is
the custom authentication request executor. This API overrides the
Spring's BASIC authentication request executor, and uses the CRXUserCredentialService to
establish a system context for API invocations. package com.adobe.livecycle.spring.remoting;
import java.io.IOException;
import java.net.HttpURLConnection;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor;
import com.adobe.livecycle.content.appcontext.AppContextManager;
import com.adobe.livecycle.crx.CRXUserCredentialService;
public class CRXAuthHttpInvokerRequestExecutor extends
AuthenticationSimpleHttpInvokerRequestExecutor {
private String appRoot;
public String getAppRoot() {
return appRoot;
}
public void setAppRoot(String appRoot) {
this.appRoot = appRoot;
}
@Override
protected void prepareConnection(HttpURLConnection con, int contentLength)
throws IOException {
boolean clearContext = false;
try {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
clearContext = true;
CRXUserCredentialService credentialService = CRXUserCredentialService
.getInstance();
UsernamePasswordAuthenticationToken ut = new UsernamePasswordAuthenticationToken(
credentialService.getUsername(),
credentialService.getPassword());
SecurityContext sctx = SecurityContextHolder
.createEmptyContext();
sctx.setAuthentication(ut);
SecurityContextHolder.setContext(sctx);
}
super.prepareConnection(con, contentLength);
con.setRequestProperty(AppContextManager.APP_CONTEXT_HEADER_NAME, appRoot);
} finally {
if (clearContext) {
SecurityContextHolder.clearContext();
}
}
}
}
|
|
|