シナリオ:LiveCycle プロセス内からの Correspondence Management サービスの呼び出し

Correspondence Management Solution API は、LiveCycle プラットフォーム(プロセスまたはオーケストレーション)内から、より大きなワークフローの一部として呼び出すことができます。

カスタムサービスコンテナを構築するには、次のライブラリが必要です。
  • 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

実装の概要

後述の例では、次のどちらかを公開するカスタムサービスコンテナを作成します(オーケストレーション内のサービスコンテナ操作)
  • Correspondence Management API を内部使用するカスタムサービス操作を公開します。

  • Correspondence Management API/操作を直接公開します。

サービスコンテナ操作としてのカスタムサービス

ある機能を実装するために Correspondence Management API を使用するカスタムサービス操作を実装し、この操作をサービスコンテナ内で公開できます。それには、次のタスクを実行します。
  1. カスタムサービスの作成

  2. bean としてのサービスの登録

  3. コンポーネントの外部化されたプロパティファイルの作成

  4. LiveCycle サービスとしてのカスタムサービスの書き出し

  5. サービスコンテナコンポーネントの定義

カスタムサービスの作成

公開された Correspondence Management API を使用するインターフェイスベースのカスタムサービスを記述し、必要なカスタムタスクを実行します。次の例では、サービスを公開し、処理された通信のレイアウトテンプレートと、処理された XML データを取得しています。このテンプレートとデータを統合し、カスタム形式で通信を生成することができます。

インターフェイス、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); 
}

実装、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; 
    } 
}

Spring bean としての登録

前述のサービスが実装されたら、(Spring ベースの)カスタムサービスコンテナ内でそのサービスを Spring bean として登録します。

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

コンポーネントの外部化されたプロパティファイルの作成

クラスパスのコンポーネント内のデフォルトのプロパティファイル(cmsa.properties)の例を次に示します。
# URL to the Spring app. 
crx.serverUrl=http://localhost:4502 
 
# more propeties here>

LiveCycle サービスとしてのカスタムサービスの書き出し

Spring Bean として使用できるようになったサービスは、LiveCycle サービスとして書き出すことができます。com.adobe.idp.dsc.component.ServiceInstanceFactory 実装(SpringServiceFactory.java)を次のように定義します。

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

この例のファクトリでは、classpath:/spring の下に beanRefContext.xml という名前の Spring 設定ファイルが必要です。このファイルは、実際のクライアントの Spring 設定または bean をロードします。次に例を示します。

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

以下の規則に従って、コンポーネントまたはサービス定義を簡素化し、Bean を LiveCycle サービスとして公開します。

  • サービスコンテナの component.xml で定義されているように、beanRefContext.xml の bean ID または名前は、コンポーネント ID と同じです。

  • サービスコンテナの component.xml で定義されているように、カスタムサービス bean の名前は LiveCycle サービス ID と同じです。

サービスコンテナコンポーネントの定義

次に、サービスコンテナコンポーネントの詳細をその component.xml で次のように定義します。

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

Correspondence Management サービスをサービスコンテナ操作として直接公開

Correspondence Management サービス全体を公開することもできます。これは既に HTTP リモートを介して公開され、その操作は LiveCycle サービスとして公開されています。カスタムサービスにラップするのではありません。例えば、FormService とその操作を公開するには、次のエントリを 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>

認証の伝達

CRX の呼び出しコンテキストを確立できます。これにより、サービスコンテナ設定を伝達して Correspondence Management API を呼び出すことができます。Correspondence Management コンテンツに対する権限を持つ、CRX ユーザー資格情報のセットを取得するカスタムサービス設定を作成します。以下の定義では、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>
設定するには:
  1. Admin user interface/サービス/アプリケーションおよびサービス/サービスの管理に移動します。

  2. カテゴリとして「Correspondence Management」を選択します。

  3. CRXUserCredentialService」サービスを選択します。

  4. CRX インスタンス用に作成し、Correspondence Management へのアクセス権のあるユーザー名とパスワードを指定します。

  5. 保存」をクリックします。

カスタム認証実行プログラム

次の CRXAuthHttpInvokerRequestExecutor.java は、カスタム認証要求実行プログラムです。この API は、Spring の BASIC 認証要求実行プログラムをオーバーライドし、 CRXUserCredentialService を使用して、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(); 
            } 
        } 
 
    } 
}