Scénario : Appel des services Correspondence Management depuis un processus LiveCycle

Vous pouvez appeler les API de la solution Correspondence Management depuis la plateforme LiveCycle (processus/orchestration) dans le cadre d’un flux de travaux plus important.

Pour créer un conteneur de service, vous avez besoin des bibliothèques suivantes :
  • 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

Présentation de l’implémentation

Les exemples ci-dessous permettent de créer un conteneur de service personnalisé qui expose l’un des éléments suivants (opérations de conteneur de service au sein de votre orchestration) :
  • les opérations de service personnalisé faisant appel en interne aux API Correspondence Management ;

  • les opérations ou API Correspondence Management, de façon directe.

Service personnalisé comme opération de conteneur de service

Vous pouvez implémenter une opération de service personnalisé qui fait appel aux API Correspondence Management pour mettre en œuvre certaines fonctionnalités, puis exposer cette opération dans votre conteneur de service. Pour ce faire, effectuez les tâches suivantes :
  1. Créez un service personnalisé.

  2. Enregistrez le service en tant que bean.

  3. Créez le fichier Externalized Property pour le composant.

  4. Exportez le service personnalisé en tant que service LiveCycle.

  5. Définissez le composant de conteneur de service.

Création d’un service personnalisé

Créez un service personnalisé basé sur une interface qui fait appel aux API Correspondence Management exposées pour effectuer la tâche personnalisée requise. Ci-dessous se trouve un exemple exposant un service pour récupérer le modèle de mise en page traité d’une correspondance et les données XML traitées, afin de les fusionner pour générer la correspondance dans un format personnalisé :

L’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); 
}

L’implémentation, 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; 
    } 
}

Enregistrement en tant que bean Spring

Une fois que le service ci-dessus est implémenté, enregistrez-le en tant que bean Spring dans votre conteneur de service personnalisé (basé sur Spring) :

<?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>

Création du fichier Externalized Property pour le composant

Ci-dessous est un exemple du fichier de propriété par défaut (cmsa.properties) au sein du composant dans le chemin de classe :
# URL to the Spring app. 
crx.serverUrl=http://localhost:4502 
 
# more propeties here>

Exportation du service personnalisé en tant que service LiveCycle

Une fois que les services sont disponibles en tant que bean Spring, vous pouvez les exporter en tant que services LiveCycle. Définissez une implémentation com.adobe.idp.dsc.component.ServiceInstanceFactory (SpringServiceFactory.java) comme suit :

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); 
        } 
    } 
}

La fabrique de l’exemple ci-dessus nécessite un fichier de configuration Spring nommé beanRefContext.xml sous classpath:/spring, qui charge la configuration ou les beans Spring du client. Par exemple :

<?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>

Suivez ces conventions pour simplifier les définitions de service ou de composant et exposer les beans en tant que service LiveCycle :

  • L’ID ou le nom de bean dans beanRefContext.xml est le même que l’ID du composant, comme défini dans le fichier component.xml du conteneur de service.

  • Le bean de service personnalisé a le même nom que l’ID de service LiveCycle, comme défini dans le fichier component.xml du conteneur de service.

Définition du composant de conteneur de service

Ensuite, définissez les détails du composant de conteneur de service dans son fichier component.xml comme suit :

<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>

Exposition des services Correspondence Management directement en tant qu’opération de conteneur de service

Vous pouvez également exposer l’intégralité du service Correspondence Management qui est déjà exposé en remoting HTTP et ses opérations en tant que service LiveCycle, plutôt qu’en tant que service personnalisé. Par exemple, pour exposer le service FormService et ses opérations, il vous suffit d’ajouter les entrées suivantes au fichier 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>

Propagation d’authentification

Vous pouvez établir un contexte de demande pour CRX pour que la configuration du conteneur de service puisse être propagée afin d’appeler les API Correspondence Management. Créez une configuration de service personnalisé qui capture un ensemble d’informations d’identification d’utilisateur CRX dont les autorisations prévalent sur le contenu Correspondence Management. La définition suivante permet d’exposer les configurations dans l’interface utilisateur de la plateforme LiveCycle.:

<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>
Etapes de configuration :
  1. Accédez à Interface administrateur > Services > Applications et services > Gestion des services.

  2. Sélectionnez la catégorie Correspondence Management.

  3. Sélectionnez le service CRXUserCredentialService.

  4. Indiquez le nom d’utilisateur et le mot de passe, que vous avez créés dans l’instance CRX, permettant d’accéder à Correspondence Management.

  5. Cliquez sur Enregistrer.

Vérificateur d’authentification personnalisé

Le fichier CRXAuthHttpInvokerRequestExecutor.java est le vérificateur de demande d’authentification personnalisé. L’API remplace le vérificateur de demande d’authentification BASIC de Spring et fait appel au service CRXUserCredentialService pour établir un contexte système pour les appels d’API.
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(); 
            } 
        } 
 
    } 
}