LiveCycle provides the ability to create components
that use custom data types as input and output values. Creating
components that support custom data types lets you create processes
that support data types that are used within your organization or
within your workflows. For example, you can create a data type that
keeps track of account information, such as the account balance.
After you create a custom data type, you can use it within LiveCycle processes.
The following illustration shows the Variable dialog box that
is accessible in Workbench. Notice that the data type of the
Customer
variable
is
com.adobe.livecycle.sample.customer.Customer
,
which is an example of a custom data type.
A variable that is based on a custom data type can be used in
processes that you create in Workbench. For example, the
Customer
variable
shown in the previous diagram can be used in processes that involves tracking
customer information.
The bank component that is created in this section is related
to a fictional bank where a new customer profile can be created
and a new account opened for the customer. This component contains
two services: Customer service and Account service.
The Customer service contains the following operations:
-
createCustomer
: Creates a new customer.
-
updateCustomer
: Updates customer information.
-
getCustomerInfo
: Retrieves information about
the customer.
-
deleteCustomer
: Deletes the customer.
The Account service contains the following operations:
-
createAccount
: Creates a new account.
-
updateAccount
: Updates account information.
-
getAccountInfo
: Retrieves information about
the account.
-
deleteAccount
: Deletes the account.
Note:
Before you create a component that uses custom
data types, it is recommended that you understand how to create
components. For information, see
Creating Your First Component
.
Sample files
This section creates Java
classes and interfaces that correspond to Java files that can be
located in the sample email component located at C:\Adobe\Adobe LiveCycle
ES4\sdk\samples\Foundation\CustomComponents\Bank,
where C:\ is the LiveCycle installation location. As you
read through this section, it is recommended that you also refer
to the sample files.
Summary of steps
To develop a component
that uses custom data types, perform the following steps:
-
Set up your development environment.
-
Create your application logic.
-
Define the component XML file.
-
Deploy the component to LiveCycle.
-
Test your component.
Setting up your development environment
The first step to create a component is to create a new
Eclipse Java project. Like other Java projects, you must add JAR
files to your project’s class path on which your Java business logic
is dependent. Because application logic within this component requires
a
com.adobe.idp.Document
object, add the adobe-livecycle-client.jar
file to your class path. For information about LiveCycle JAR files, see
Including LiveCycle Java library files
Note:
The bank component does not require JAR files
other than adobe-livecycle-client.jar.
Creating your application logic
Defining the customer service interface
To develop the bank component, you must create the customer service
interface, which defines the service’s operations, input values,
and return values. This service has four operations, all of which
must be defined in the service interface. The following Java code
example shows the customer interface that belongs to the bank component.
Notice that the name of the Java interface is
CustomerService
and
is located in the
com.adobe.livecycle.sample.customer
package.
Defining the customer service interface
package com.adobe.livecycle.sample.customer;
/**
* The Customer service provides methods to
* create a new Customer object, delete
* the Customer, and update the customer information. Information
* about the customer can also be retrieved.
*/
public interface CustomerService {
/**
* Create a new customer
* @param inCustomer Customer object containing information about the customer
* @return customerId unique string identifying the customer
* @exception CustomerException if any of the fields in the Customer object are missing
*/
public String createCustomer(Customer inCustomer)throws CustomerException;
/**
* Delete the customer
* @param inCustomerId unique string identifying the customer
* @exception CustomerException if customer does not exist
*/
public void deleteCustomer(String inCustomerId)throws CustomerException;
/**
* Update the customer
* @param inCustomer Customer object
* @exception CustomerException if customer does not exist
*/
public void updateCustomer(Customer inCustomer)throws CustomerException;
/**
* Get the customer information
* @param inCustomerId id of customer
* @return Customer information
* @exception CustomerException if customer does not exist
*/
public Customer getCustomerInfo(String inCustomerId)throws CustomerException;
}
Defining the account service interface
To develop the bank component, create the account service interface,
which defines the account service’s operations, input values, and return
values. This service has four operations, all of which must be defined
in the service interface. The following Java code example shows
the account interface that belongs to the bank component. Notice
that the name of the Java interface is
AccountService
and
it belongs to the
com.adobe.livecycle.sample.account
package.
Defining the account service interface
package com.adobe.livecycle.sample.account;
/**
* Adds a new account for a specific customer
*/
public interface AccountService {
/**
* Creates a new bank account
* @param accountType An enumeration of different kinds of accounts
* @param accountInfo An account information object
* @return The identifier value of the new account
* @exception AccountException if account information is missing
*/
public long createNewAccount(AccountTypes accountType, AccountInfo accountInfo) throws AccountException;
/**
* Removes the account
* @param accountId The identifier of the account
* @exception AccountException if the account is not found
*/
public void removeAccount(long accountId)throws AccountException;
/**
* Update the account information
* @param accountInfo Specifies the updated account information
* @exception AccountException if account is not found
*/
public void updateAccount(AccountInfo accountInfo)throws AccountException;
/**
* Get account information
* @param accountId The identifier of the account from which to retrieve information
* @return Ab AccountInfo object containing account information
* @exception AccountInfo if account is not found
*/
public AccountInfo getAccountInfo(long accountId)throws AccountException;
}
Defining the Customer class
Create the
Customer
class that implements the
java.io.Serializable
abstract
interface. All user-defined objects must follow the Java Bean requirement
of setter and getter methods and implement
java.io.Serializable
. Objects
of this type are used by the Customer service.
The
Customer
class exposes the following data members:
-
city
-
customerId
-
name
-
phone
-
state
-
street
-
zip
The following example shows the
Customer
class that
belongs to the
com.adobe.livecycle.sample.customer
package.
Defining the Customer Java Bean
package com.adobe.livecycle.sample.customer;
/**
* The Customer object holds personal information
* about a customer. An object of this type is passed to the
* Customer service to register a new customer.
*
*/
public class Customer implements java.io.Serializable {
//Specifies the name of the customer
private String name;
//Specifies the street of the customer
private String street;
//Specifies the customer's city
private String city;
//Specifies customer's state
private String state;
//Specifies the customer's zip code
private int zip;
//Specifies the customer's phone number
private String phone;
//Specifies the customer's identifier value
private String customerId;
//Returns the customer's city
public String getCity() {
return city;
}
//Sets the customer's city
public void setCity(String city) {
this.city = city;
}
//Gets the customer's phone number
public String getPhone() {
return phone;
}
//Sets the customer's phone number
public void setPhone(String phone) {
this.phone = phone;
}
//Gets the customer's state
public String getState() {
return state;
}
//Sets the customer's state
public void setState(String state) {
this.state = state;
}
//Gets the customer's street
public String getStreet() {
return street;
}
//Sets the customer's street
public void setStreet(String street) {
this.street = street;
}
//Gets the customer's ZIP code
public int getZip() {
return zip;
}
//Sets the customer's ZIP code
public void setZip(int zip) {
this.zip = zip;
}
//Gets the customer's identifier value
public String getCustomerId() {
return customerId;
}
//Sets the customer's identifier value
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
//Gets the customer's name
public String getName() {
return name;
}
//Sets the customer's name
public void setName(String name) {
this.name = name;
}
}
Defining the Account class
Create the
AccountInfo
class, which is
a Java Bean that implements the
java.io.Serializable
abstract
interface. All user-defined objects must follow the Java Bean requirement
of setter and getter methods and implement
java.io.Serializable
. Objects
of this type are used by the Account service.
The
AccountInfo
class exposes the following
data members:
-
accountBalance
-
accountId
-
accountOpenedDate
-
accountType
-
customerId
-
newAccountSignatureForm
The following example shows the
AccountInfo
class
that belongs to the
com.adobe.livecycle.sample.account
package.
package com.adobe.livecycle.sample.account;
import java.util.Date;
import com.adobe.idp.Document;
// The AccountInfo object holds bank account information for the customer
public class AccountInfo implements java.io.Serializable {
//Specifies the date when the account was created
private Date accountOpenedDate;
//Specifies the identifier value of the account
private long accountId;
//Specifies the account balance
private double accountBalance;
//Specifies the account form
private Document newAccountSignatureForm;
//Specifies the identifier of the custom who opened the account
private String customerId;
//Specifies the type of account
private String accountType;
//Gets the type of account
public String getAccountType() {
return accountType;
}
//Sets the type of account
public void setAccountType(String accountType) {
this.accountType = accountType;
}
//Gets the customer identifier value
public String getCustomerId() {
return customerId;
}
//Sets the customer identifier value
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
//Gets the account form
public Document getNewAccountSignatureForm() {
return newAccountSignatureForm;
}
//Sets the account form
public void setNewAccountSignatureForm(Document newAccountSignatureForm) {
this.newAccountSignatureForm = newAccountSignatureForm;
}
//Gets the identifier of the account
public long getAccountId() {
return accountId;
}
//Sets the identifier of the account
public void setAccountId(long accountId) {
this.accountId = accountId;
}
//Gets the date when the account was opened
public Date getAccountOpenedDate() {
return accountOpenedDate;
}
//Sets the date when the account was opened
public void setAccountOpenedDate(Date accountOpenedDate) {
this.accountOpenedDate = accountOpenedDate;
}
//Gets the balance of the account
public double getAccountBalance() {
return accountBalance;
}
//Sets the balance of the account
public void setAccountBalance(double accountBalance) {
this.accountBalance = accountBalance;
}
}
Defining the Account type enumeration values
The bank component uses a Java enumeration value named
AccountType
that
specifies the different account types. This enum value is used as
an input value for the Account service’s
createNewAccount
operation.
The following account types are defined:
-
Savings
-
RegularChecking
-
MoneyMarket
-
InterestChecking
-
CertificateOfDeposit
The following Java code shows a Java enum named
AccountTypes
and
belongs to the
com.adobe.livecycle.sample.account
package.
package com.adobe.livecycle.sample.account;
/**
* The AccountTypes enum value is used as input values for
* operations that belong to the Account service.
*/
public enum AccountTypes {
Savings, RegularChecking, MoneyMarket, InterestChecking,
CertificateOfDeposit;
}
Creating user-defined exceptions
Creating user-defined exceptions is a way of distinguishing
exceptions thrown by your own services. It is optional because you
can rely on the built-in
java.lang.Exception
object
to be thrown from your methods. The bank component created in this
section uses two user-defined exceptions:
The following Java code creates the
CustomerException
.
package com.adobe.livecycle.sample.customer;
/**
* CustomerException throws Customer service specific exceptions.
*
*/
public class CustomerException extends Exception {
//Constructor
public CustomerException() {
super();
}
public CustomerException(String message){
super(message);
}
}
The following Java code creates the
AccountException
.
package com.adobe.livecycle.sample.account;
/**
* AccountException throws Account service specific exceptions.
*
*/
public class AccountException extends Exception {
//Constructor
public AccountException() {
super();
}
public AccountException(String message){
super(message);
}
}
Defining the customer service implementation
You must create the customer service’s implementation class
that extends the
Customer
interface. For information,
see
Defining the customer service interface
.
The business logic that is executed by LiveCycle when
the operation is invoked must be specified in the corresponding
methods. For example, consider the
createCustomer
method
that is defined in the
CustomerService
interface.
You must define business logic within the
createCustomer
method
that creates a new customer.
The customer service implementation defines application logic
for the following methods.
-
createCustomer
: Creates a new customer.
-
updateCustomer
: Updates customer information.
-
getCustomerInfo
: Retrieves information about
the customer.
-
deleteCustomer
: Deletes the customer.
The following Java code example creates the customer implementation class
named
CustomerServiceImpl
and implements the
CustomerService
interface.Defining
the customer service implementation.
package com.adobe.livecycle.sample.customer;
import java.util.HashMap;
/**
* The Customer service creates a new Customer object, deletes
* the Customer and updates the Customer information. Information
* about the Customer can also be retrieved. Typically,
* customer information is stored in a database, however
* for demonstration purposes, customer information will be stored
* in a HashMap.
*/
public class CustomerServiceImpl implements CustomerService {
/**
* Keep the customer information in memory. Normally,
* this information would be in a database.
*/
private static HashMap customerMap;
//Creates a new customer
public String createCustomer(Customer inCustomer)throws CustomerException {
if (inCustomer == null){
//invalid Customer object
}
else{
if (customerMap == null){
customerMap = new HashMap();
}
//Validate that the Customer object has data in it.
if (isEmpty(inCustomer.getName()))
{
//throw an exception that Name is required.
throw new CustomerException("Customer name is required");
}
//Validate that the Address object is not null.
if (isEmpty(inCustomer.getStreet()) ||
isEmpty(inCustomer.getCity()) || isEmpty(inCustomer.getState()))
{
//throw an exception that the Address is required
throw new CustomerException("Customer address information is required");
}
//generate unique a customer id
inCustomer.setCustomerId(inCustomer.getName() + System.currentTimeMillis());
//add the new customer.
customerMap.put(inCustomer.getCustomerId(), inCustomer);
}
return inCustomer.getCustomerId();
}
//Deletes the specified customer
public void deleteCustomer(String customerId) throws CustomerException{
if (customerMap == null){
throw new CustomerException("No customers exist");
}
if (!(customerMap.containsKey(customerId)))
{
throw new CustomerException("The customer does not exist");
}
customerMap.remove(customerId);
}
//Retrieves customer information
public Customer getCustomerInfo(String customerId)throws CustomerException {
if (customerMap == null){
throw new CustomerException("No customers exist");
}
if (!(customerMap.containsKey(customerId)))
{
throw new CustomerException("The customer does not exist");
}
return (Customer)customerMap.get(customerId);
}
//Updates customer information
public void updateCustomer(Customer customer)throws CustomerException {
if (customerMap == null){
throw new CustomerException("No customers exist");
}
if (!(customerMap.containsKey(customer.getCustomerId())))
{
throw new CustomerException("The customer does not exist");
}
customerMap.put(customer.getCustomerId(), customer);
}
//Determines whether the specified string contains only non whitespace
private boolean isEmpty(String aValue) {
return (aValue == null || aValue.trim().length() == 0);
}
}
Defining the account service implementation
You must create the Account service’s implementation class
that extends the
Account
interface. For information,
see
Defining the account service interface
.
The business logic that is executed by LiveCycle when
the operation is invoked must be specified in the corresponding
methods. For example, consider the
createAccount
method
that is defined in the
AccountService
interface.
You must define business logic within the
createAccount
method
that creates a new account.
The account service implementation defines application logic
for the following methods:
-
createAccount
: Creates a new account.
-
updateAccount
: Updates account information.
-
getAccountInfo
: Retrieves information about
the account.
-
deleteAccount
: Deletes the account.
The following Java code example creates the customer implementation class
named
AccountServiceImpl
and implements the
AccountService
interface.
package com.adobe.livecycle.sample.account;
import java.util.HashMap;
/**
* This object implements the AccountService interface.
* The Account Service performs operations related to a bank account
*/
public class AccountServiceImpl implements AccountService {
/**
* Stores all the account in memory. Usually this is
* stored in a database.
*/
private static HashMap accountMap;
//Creates a new account
public long createNewAccount(AccountTypes accountType,
AccountInfo accountInfo) throws AccountException{
//Allocate memory for storing the account information.
if (accountMap == null)
{
accountMap = new HashMap();
}
//Validate the data in the AccountInfo.
if (accountInfo == null){
throw new AccountException("No Account Information provided");
}
if (accountInfo.getCustomerId() == null)
{
throw new AccountException("No customer id provided");
}
if (accountInfo.getNewAccountSignatureForm() == null){
throw new AccountException("Please provide the account signature form");
}
if (accountType != null)
accountInfo.setAccountType(accountType.toString());
//For demonstration purposes, accountId is the current timestamp.
accountInfo.setAccountId(System.currentTimeMillis());
//The account id is the key to lookup the account information.
accountMap.put(accountInfo.getAccountId(), accountInfo);
return accountInfo.getAccountId();
}
//Returns information about the specified account
public AccountInfo getAccountInfo(long accountId)throws AccountException {
if (accountMap == null){
throw new AccountException("No accounts exist");
}
if (!accountMap.containsKey(accountId))
throw new AccountException("Account does not exist");
return (AccountInfo)accountMap.get(accountId);
}
//Deletes the specified account
public void removeAccount(long accountId)throws AccountException {
if (accountMap == null){
throw new AccountException("No accounts exist");
}
if (!accountMap.containsKey(accountId))
throw new AccountException("Account does not exist");
accountMap.remove(accountId);
}
//Updates the account
public void updateAccount(AccountInfo accountInfo)throws AccountException {
if (accountMap == null){
throw new AccountException("No accounts exist");
}
if (!accountMap.containsKey(accountInfo.getAccountId()))
throw new AccountException("Account does not exist");
//Get the stored account information.
AccountInfo storedAccount = (AccountInfo)accountMap.get(accountInfo.getAccountId());
//Since we are only allowing the balance to be updated, set just the balance.
storedAccount.setAccountBalance(accountInfo.getAccountBalance());
//Save the stored account after it's been updated. All other fields
//should remain the same.
accountMap.put(accountInfo.getAccountId(), storedAccount);
}
}
Defining the service’s LifeCycle implementation
You can create a LifeCycle implementation class that lets
you control the component’s behavior when it is started and stopped.
Within the LifeCycle implementation class, you can create business
logic to meet your business requirements. Adding a LifeCycle implementation
class is optional.
The LifeCycle implementation class must extend the
com.adobe.idp.dsc.component.LifeCycle
interface
and expose the following methods:
-
setComponentContext
: Sets the context
of the component.
-
onStart
: Is invoked when the service is started.
-
onStop
: Is invoked when the component is stopped.
The following Java example shows the LifeCycle implementation
class named
LifeCycleImpl
that extends
LifeCycle
.
This implementation class writes messages to the J2EE application
server’s log file when the service is started or stopped. For information
about a component’s behavior when it is started and stopped, see “Component
States” in
Developing Components for LiveCycle
.
package com.adobe.livecycle.sample.bank;
import java.util.logging.Level;
import com.adobe.idp.dsc.component.ComponentContext;
import com.adobe.idp.dsc.component.LifeCycle;
import com.adobe.logging.AdobeLogger;
//Controls onStart and onStop behavior.
public class LifeCycleImpl implements LifeCycle {
private static final AdobeLogger logger = AdobeLogger
.getAdobeLogger(LifeCycleImpl.class);
private ComponentContext m_ctx;
//Sets the component's context
public void setComponentContext(ComponentContext aContext) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "setComponentContext: "
+ aContext.getComponent().getComponentId());
}
m_ctx = aContext;
}
// Invoked when the component is started
public void onStart() {
logger.log(Level.INFO, "Called onStart: "
+ m_ctx.getComponent().getComponentId());
}
//Invoked when the component is stopped
public void onStop() {
logger.log(Level.INFO, "Called onStop: "
+ m_ctx.getComponent().getComponentId());
}
}
Defining the service’s Bootstrap implementation
You can create a Bootstrap implementation class that lets
you control the component’s behavior when it is installed and uninstalled.
Within the Bootstrap implementation class, you can create business
logic to meet your business requirements. Adding a Bootstrap implementation
class is optional.
The Bootstrap implementation class must extend the
com.adobe.idp.dsc.component.Bootstrap
interface
and expose the following methods:
-
setBootstrapContext
: Is invoked when
the component is installed and a
BootstrapContext
instance
is passed as an argument.
-
onInstall
: Is invoked when the service is started.
-
onUnInstall
: Is invoked when the component
is stopped.
The following Java example shows the Bootstrap implementation
class named
BootstrapImpl
that extends
Bootstrap
and
is located within the
com.adobe.livecycle.sample.bank
package.
package com.adobe.livecycle.sample.bank;
import java.util.logging.Level;
import com.adobe.idp.dsc.component.Bootstrap;
import com.adobe.idp.dsc.component.BootstrapContext;
import com.adobe.logging.AdobeLogger;
//Controls onInstall and onUninstall behavior.
public class BootstrapImpl implements Bootstrap{
private static final AdobeLogger logger = AdobeLogger.getAdobeLogger(BootstrapImpl.class);
private BootstrapContext m_ctx;
public void setBootstrapContext(BootstrapContext aCtx) {
logger.log(Level.INFO, "Set bootstrap context: " + aCtx.getComponent().getComponentId());
m_ctx = aCtx;
}
//Invoked when the component is uninstalled
public void onUnInstall() {
logger.log(Level.INFO, "Called onUnInstall: " + m_ctx.getComponent().getComponentId());
}
//Invoked when the component is installed
public void onInstall() {
logger.log(Level.INFO, "Called onInstall: " + m_ctx.getComponent().getComponentId());
}
}
Defining the component XML file for the bank component
You must create a component XML file in order to create
a component that can be deployed to LiveCycle. A component
XML file exists for every component and provides metadata about
the component and the services contained in it.
Composite editors
When creating a component that uses custom data
types, you must define the component’s composite editor so that
the component can handle custom data types. Parameters that expect
primitive data types, such as a string, are supported through basic
property editors. However, input parameters that expect complex
or custom data types must implement a composite editor, which is
a property editor that enables the usage of one or more nested property editors
together as a single UI to a complex data structure and the attributes
and objects within it. Composite property editors are not created
using Java code; instead, they are configured declaratively within
the component XML file.
When creating a composite editor within
the component XML file, you must assign the fully qualified name
of the Java class that corresponds to the composite editor to the
composite-type
element. Likewise,
you must assign an identifier value to the composite editor by using
the
composite-editor id
attribute. Finally, you
must specify the data members within the Java class by using the
attribute element, as shown in the following example:
<composite-editor id="com.adobe.livecycle.sample.customer.Customer">
<composite-type>com.adobe.livecycle.sample.customer.Customer</composite-type>
<attributes>
<!-- the names in the attribute list must match the names of the fields in the
Customer object -->
<attribute name="name" title="First and Last Name" />
<attribute name="street" title="Street" />
<attribute name="city" title="City" />
<attribute name="state" title="State" />
<attribute name="zip" title="Zip Code" />
<attribute name="phone" title="Phone Number" />
</attributes>
</composite-editor>
Notice that these six attributes
correspond to the data members defined within the
Customer
class.
For information, see
Defining the Customer class
.
You reference a composite editor
when you define a service operation’s input values. For example,
a
Customer
object must be passed as an input value
to the
Customer
service’s
createCustomer
operation.
As a result, you must reference the
com.adobe.livecycle.sample.customer.Customer
composite
editor when you define input values for the
createCustomer
operation,
as shown in the following example:
<operation name="createCustomer">
<input-parameter name="inCustomer"
title="Customer" type="com.adobe.livecycle.sample.customer.Customer">
<!-- Use the new property-editor type we defined -->
<property-editor editor-id="com.adobe.livecycle.sample.customer.Customer" />
<hint>Create a new customer</hint>
</input-parameter>
Notice that the data type
of the
inCustomer
parameter is
com.adobe.livecycle.sample.customer.Customer
.
When
Workbench users enter literal values for the
createCustomer
operation,
they can specify a value for each field within the
Customer
class.
Notice that the displayed text matches the value of the title attribute
defined within the composite editor.
enum property editor
You can define enum input values for service
operations. For example, consider the Account service’s
createNewAccount
operation.
This operation requires an
AccountTypes
enum value.
To
handle an enum value as an input value, you must use an enum property editor.
Like a composite editor, you reference an enum property editor when defining
a service operation’s input values. Therefore, because the
createNewAccount
operation
requires an
AccountTypes
enum value as an input
value, you must reference a
com.adobe.idp.dsc.propertyeditor.system.Enum
property
editor when you define input values for the
createNewAccount
operation,
as shown in the following example:
<operation name="createNewAccount">
<!-- Note that the AccountTypes is a enum object -->
<input-parameter name="accountType" title="Account Type" type="com.adobe.livecycle.sample.account.AccountTypes">
<!-- The enum property editor provides a pull down menu of all the
available strings in the AccountTypes object -->
<property-editor editor-id="com.adobe.idp.dsc.propertyeditor.system.Enum" />
</input-parameter>
When Workbench users enter
literal values for the createNewAccount operation, they must select
a value from a drop-down list. Each value in the list corresponds
to an enum value that is defined in
com.adobe.livecycle.sample.account.AccountTypes
.
Determining the order of input values
You can specify the order in
which input and output values are displayed within Workbench by
using the
layout
element. For example, consider
the
createNewAccount
operation. This operation
requires an
AccountInfo
object, which contains
the following five field values that correspond to data members
defined within the
AccountInfo
class:
accountId
,
accountOpenedDate
,
customerId
,
accountBalance
, and
newAccountSignatureForm
.
You
can define the order in which these fields are displayed by using
the
layout
element, as shown in the following example:
<!-- Using the layout element component developers can specify the
grouping and order of their Property Editors -->
<layout>
<section name="Account Type">
<property ref="input/accountType" />
</section>
<section name="Customer Id">
<property ref="input/accountInfo/customerId" />
</section>
<section name="Account Balance">
<property ref="input/accountInfo/accountBalance" />
</section>
<section name="New Account Signature Form">
<property ref="input/accountInfo/newAccountSignatureForm" />
</section>
<section name="Date Account Opened">
<property ref="input/accountInfo/accountOpenedDate" />
</section>
<section name="Returned Account Id">
<property ref="output/AccountId" />
</section>
</layout>
The following illustration shows
the results of this layout.
Note:
In the illustration above, accountId is a process
variable of type long.
Using the Date property editor
Within the component XML file,
you can declare a Date property editor when an input value requires
a
java.util.Date
value. For example, the
AccountInfo
class
contains a data member named
accountOpenedDate
that
requires a
java.util.Date
value. For information,
see
Defining the Account class
.
In this situation, define a Date
property editor within the
com.adobe.livecycle.sample.account.AccountInfo
composite
editor, as shown in the following example:
<composite-editor id="com.adobe.livecycle.sample.account.AccountInfo">
<composite-type>com.adobe.livecycle.sample.account.AccountInfo</composite-type>
<attributes>
<attribute name="accountId" title="Account Id"/>
<attribute name="accountOpenedDate" title="Date Account Opened">
<!-- Using the CalendarPropertyEditorComponent will automatically fill the field with today's date-->
<property-editor editor-id= "com.adobe.idp.dsc.propertyeditor.system.CalendarPropertyEditorComponent"/>
</attribute>
<attribute name="customerId" title="Customer Id"/>
<attribute name="accountBalance" title="Account Balance"/>
<attribute name="newAccountSignatureForm" title="New Account Signature Form"/>
</attributes>
</composite-editor>
XML elements
The component XML file that is used for the bank
component contains the following XML elements:
-
component-id
:
Specifies a unique identifier for the component.
-
version
: Specifies the version of the component.
-
class-path
: Specifies JAR files that are required
by the component. For JAR files to be used by the component, they
must be specified within this element.
-
bootstrap-class
: Specifies the fully qualified name
of the Bootstrap implementation class.
-
lifecycle-class
: Specifies the fully qualified name
of the LifeCycle implementation class.
-
editors
:
Specifies the editors used within
this component.
-
composite-editor
: Specifies the identifier value for
this composite editor.
-
composite-type
: Specifies the fully qualified name
of the Java class (custom data type) that corresponds to the composite editor.
-
export-packages
: Specifies that the packages
com.adobe.livecycle.sample.customer
and
com.adobe.livecycle.sample.account
are exported.
For example, when invoking this service using its WSDL, these two packages
are exported. This element is located at the root level of the component
XML file.
-
services
: Specifies the services that are part of
this component. This element can specify many
service
elements.
-
service
: Specifies the name of the service.
-
implementation-class
: Specifies the name of the implementation
class for the service.
-
small-icon
: Specifies the small icon that is used
to display the service in the toolbar and Workbench.
-
large-icon
: Specifies the large icon that is used
to display the service.
-
operations
: Specifies the operations that are part
of this service. This element can contain one or many
operation
elements.
-
operation
: Specifies the operation name.
-
input-parameter
: Specifies the name and type of the
input parameter for the specified operation. An input parameter
element must exist for each parameter that corresponds to the operation.
As well, each parameter’s data type must be specified using the
type
attribute.
-
output-parameter
: Specifies the return value of an operation.
The data type must be specified using the
type
attribute.
-
hint
: Specifies a message that is displayed when the
user clicks the property name in the property editor.
The
following component.xml file is used for the bank component. Notice that
this component XML file contains two services named
CustomerService
and
AccountService
.
Defining the component XML file for the Bank component
<component xmlns="http://adobe.com/idp/dsc/component/document">
<!-- Unique ID identifying this DSC -->
<component-id>com.adobe.livecycle.sample.bank.bankComponent</component-id>
<!-- Version -->
<version>1.0</version>
<!-- Bootstrap implementation class -->
<bootstrap-class>com.adobe.livecycle.sample.bank.BootstrapImpl</bootstrap-class>
<!-- LifeCycle implementation class -->
<lifecycle-class>com.adobe.livecycle.sample.bank.LifeCycleImpl</lifecycle-class>
<!-- When using user-defined types, use the composite editor to define the -->
<!-- panel in which users can enter the input data for the fields of the complex type-->
<editors>
<composite-editor id="com.adobe.livecycle.sample.customer.Customer">
<composite-type>com.adobe.livecycle.sample.customer.Customer</composite-type>
<attributes>
<!-- the names in the attribute list must match the names of the fields in the
Customer object -->
<attribute name="name" title="First and Last Name"/>
<attribute name="street" title="Street"/>
<attribute name="city" title="City"/>
<attribute name="state" title="State"/>
<attribute name="zip" title="Zip Code"/>
<attribute name="phone" title="Phone Number"/>
</attributes>
</composite-editor>
<composite-editor id="com.adobe.livecycle.sample.account.AccountInfo">
<composite-type>com.adobe.livecycle.sample.account.AccountInfo</composite-type>
<attributes>
<attribute name="accountId" title="Account Id"/>
<attribute name="accountOpenedDate" title="Date Account Opened">
<!-- Using the CalendarPropertyEditorComponent will automatically fill the
field with today's date-->
<property-editor editor-id=
"com.adobe.idp.dsc.propertyeditor.system.CalendarPropertyEditorComponent"/>
</attribute>
<attribute name="customerId" title="Customer Id"/>
<attribute name="accountBalance" title="Account Balance"/>
<attribute name="newAccountSignatureForm" title="New Account Signature Form"/>
</attributes>
</composite-editor>
</editors>
<export-packages>
<package version="1.0">com.adobe.livecycle.sample.customer</package>
<package version="1.0">com.adobe.livecycle.sample.account</package>
</export-packages>
<!-- Start of the Service definition -->
<services>
<!-- Unique name for the service descriptor. The value is used as the default name for deployed services -->
<service name="CustomerService">
<!-- service implementation class definition -->
<implementation-class>
com.adobe.livecycle.sample.customer.CustomerServiceImpl</implementation-class>
<!-- description -->
<description>Allows the creation, deletion and update of Customer information.</description>
<!-- You can provide your own icons for a distinct look -->
<small-icon>icons/C_CustomerService_md.gif</small-icon>
<large-icon>icons/C_CustomerService_xl.gif</large-icon>
<!-- Automatically deploys the service and starts it after installation -->
<!-- Note that category-id indicates where the service will show up in the existing categories in
the Services view in workbench -->
<auto-deploy category-id="example" major-version="1" minor-version="0"
service-id="CustomerService"/>
<operations>
<!-- method name in the interface -->
<operation name="createCustomer">
<input-parameter name="inCustomer" title="Customer"
type="com.adobe.livecycle.sample.customer.Customer">
<!-- Use the new property-editor type we defined -->
<property-editor editor-id=
"com.adobe.livecycle.sample.customer.Customer"/>
<hint>Create a new customer </hint>
</input-parameter>
<!-- The customer ID is returned from the createCustomer call-->
<output-parameter name="CustomerId" type="java.lang.String"/>
<faults>
<fault name="CustomerException" type=
"com.adobe.livecycle.sample.customer.CustomerException"/>
</faults>
<hint>Creates a new customer</hint>
</operation>
<operation name="deleteCustomer">
<input-parameter name="inCustomerId" title="Customer Identification String"
type="java.lang.String">
<hint>Delete a customer </hint>
</input-parameter>
<faults>
<fault name="CustomerException" type=
"com.adobe.livecycle.sample.customer.CustomerException"/>
</faults>
</operation>
<operation name="updateCustomer">
<input-parameter name="inCustomer" title="Customer" type=
"com.adobe.livecycle.sample.customer.Customer">
<property-editor editor-id=
"com.adobe.livecycle.sample.customer.Customer"/>
<hint>Update customer information</hint>
</input-parameter>
<faults>
<fault name="CustomerException" type=
"com.adobe.livecycle.sample.customer.CustomerException"/>
</faults>
</operation>
<operation name="getCustomerInfo">
<input-parameter name="inCustomerId" title="Customer Identification String"
type="java.lang.String">
<hint>Get customer information</hint>
</input-parameter>
<output-parameter name="Customer" type=
"com.adobe.livecycle.sample.customer.Customer">
<!-- Even though we are defining a property-editor type for our
returned value, this really does not affect the way the data is
returned visually. Users will still need to use the Xpath expression
mechanism to get each field out
of the Customer object for output -->
<property-editor editor-id=
"com.adobe.livecycle.sample.customer.Customer"/>
<hint>Returned value is a Customer</hint>
</output-parameter>
<faults>
<fault name="CustomerException" type=
"com.adobe.livecycle.sample.customer.CustomerException"/>
</faults>
</operation>
</operations>
</service>
<!-- Start of second service-->
<service name="AccountService">
<implementation-class>
com.adobe.livecycle.sample.account.AccountServiceImpl
</implementation-class>
<description>Allow a new account to be created for a given customer</description>
<small-icon>icons/C_AccountService_md.gif</small-icon>
<large-icon>icons/C_AccountService_xl.gif</large-icon>
<!-- Put this new service under the "example" category in the Services view -->
<auto-deploy category-id="example" major-version="1" minor-version="0"
service-id="AccountService"/>
<operations>
<operation name="createNewAccount">
<!-- Note that the AccountTypes is a enum object -->
<input-parameter name="accountType" title="Account Type" type=
"com.adobe.livecycle.sample.account.AccountTypes">
<!-- The enum property editor provides a pull down menu of all the
available strings in the AccountTypes object -->
<property-editor editor-id=
"com.adobe.idp.dsc.propertyeditor.system.Enum"/>
</input-parameter>
<input-parameter name="accountInfo" title="Account Information" type=
"com.adobe.livecycle.sample.account.AccountInfo">
<!-- Use the new property editor we defined for the Account
object -->
<property-editor editor-id=
"com.adobe.livecycle.sample.account.AccountInfo"/>
<hint>Create a new account </hint>
</input-parameter>
<output-parameter name="AccountId" type="long"/>
<faults>
<fault name="AccountException" type=
"com.adobe.livecycle.sample.account.AccountException"/>
</faults>
<!-- Using the layout element component developers can specify the
grouping and order of their Property Editors -->
<layout>
<section name="Account Type">
<property ref="input/accountType"/>
</section>
<section name="Customer Id">
<property ref="input/accountInfo/customerId"/>
</section>
<section name="Account Balance">
<property ref="input/accountInfo/accountBalance"/>
</section>
<section name="New Account Signature Form">
<property ref=
"input/accountInfo/newAccountSignatureForm"/>
</section>
<section name="Date Account Opened">
<property ref="input/accountInfo/accountOpenedDate"/>
</section>
<section name="Returned Account Id">
<property ref="output/AccountId"/>
</section>
</layout>
</operation>
<operation name="removeAccount">
<input-parameter name="accountId" title="Account Information Id"
type="long">
<hint>Remove the account </hint>
</input-parameter>
<faults>
<fault name="AccountException" type=
"com.adobe.livecycle.sample.account.AccountException"/>
</faults>
</operation>
<operation name="updateAccount">
<input-parameter name="accountInfo" title="Account Information"
type="com.adobe.livecycle.sample.account.AccountInfo">
<property-editor editor-id=
"com.adobe.livecycle.sample.account.AccountInfo"/>
<hint>Update the account </hint>
</input-parameter>
<faults>
<fault name="AccountException" type=
"com.adobe.livecycle.sample.account.AccountException"/>
</faults>
<!-- Using the layout element component developers can specify the
grouping and order of their Property Editors. This time only display the
accountBalance as the only field that can be updated.-->
<layout>
<section name="Account Id">
<property ref="input/accountInfo/accountId"/>
</section>
<section name="Account Balance">
<property ref="input/accountInfo/accountBalance"/>
</section>
</layout>
</operation>
<operation name="getAccountInfo">
<input-parameter name="accountId" title="Account Information Id"
type="long">
<hint>Get the account information</hint>
</input-parameter>
<!-- Alternatively you can use the AccountInfo property-editor we defined, but
that won't change the fact for output, one has to use the xpath expression to get
the individual fields out of the AccountInfo object-->
<output-parameter name="AccountInfo" type=
"com.adobe.livecycle.sample.account.AccountInfo">
<hint>The returned Account information</hint>
</output-parameter>
<faults>
<fault name="AccountException" type=
"com.adobe.livecycle.sample.account.AccountException"/>
</faults>
</operation>
</operations>
</service>
</services>
</component>
Note:
The bank component
does not define configuration values.
Deploying the bank component
To deploy the bank component to LiveCycle, you
must package your Eclipse project into a JAR file. You must ensure
that the adobe-livecycle-client.jar file and the component XML file
are located at the root of the JAR file.
The following illustration shows the Eclipse project’s content,
which is packaged into the bank component’s JAR file.
Package the email component into a JAR file named bank.jar. In
the above illustration, notice that JAVA files are listed. After
packaged into a JAR file, the corresponding CLASS files must also
be specified. Without the CLASS files, the component will not work.
Note:
After you package the bank component into a
JAR file, you can deploy the component to LiveCycle. For
information, see
Deploying the email component
.
Testing the bank component
After you deploy the bank component to LiveCycle,
you can test it by creating a new process that uses it. Although
this section does not explain all the concepts involved in creating
a process, it provides the basic concepts for you to create a simple
process that uses the bank component.
The following illustration shows an example process named
CreateBankAccount
that
creates a new customer, creates a new account, and then emails a
message that specifies the new customer and account identifier value.
The email component created in the previous section is used to email
a message to the user. For information, see
Creating Your First Component
.
The following table describes the operations that are used within
this process.
Operation
|
Description
|
CreateCustomer
|
Creates a new customer.
|
CreateAccount
|
Creates a new account.
|
SetValue
|
Sets values that are used within this process
|
Send
|
Sends an email message to an email recipient.
|
Defining values for the createCustomer operation
The following
illustration shows the property editor that corresponds to the
createCustomer
operation.
Notice
that these input values correspond to the fields that are located
in the
Customer
class. For information, see
Defining the Customer class
.
The
createCustomer
operation
returns a string value that represents the customer identifier value.
This value is stored in a string process variable named
customerId
.
Defining values for the createAccount operation
The following illustration
shows the property editor that corresponds to the
createAccount
operation.
Notice
that these input values correspond to the fields that are located
in the
AccountInfo
class. For information, see
Defining the Account class
.
The customer identifier input value
is assigned the value of the
customerId
process
variable. The
createAccount
operation returns a
string value that represents the account identifier value. This
value is stored in another string process variable named
accountId
.
Defining values for the setValue operation
The
setValue
operation
assigns a string process variable named
outputResult
with
the value of
customerId
and
accountID
.
The following XPath expression is used to concatenate these string
values together:
concat( "The customer ID is ", /process_data/@customerId,
"The bank account IDs ", /process_data/@accountId)
The
following illustration shows the mapping property editor that corresponds
to the
SetValue
operation.
Defining values for the send operation
The
send
operation
(which belongs to the email component created in the previous section)
emails the value of the
outputResult
process variable
to the specified email recipient. For information about the send
operation, see
Creating Your First Component
.
The following illustration shows
the property editor that corresponds to the
send
operation.
To create a process that uses the bank component:
-
Select
File
>
New Process
.
-
Type
CreateBankAccount
for the process name
and accept the default settings.
-
In the
Services
view, drag the
CreateCustomer
,
CreateAccount
,
SetValue
, and
Send
operations
onto the process diagram (these operations are shown in the sample
process introduced in this section).
-
Click the
Properties
view and confirm that default property
editors have been assigned to each parameter. Enter values in the property
editor.
-
Activate the process.
Note:
The
process created in this section is named
CreateBankAccount
and
contains one operation named
invoke
. The application
logic to programmatically invoke this process is the same as the
application logic to invoke the
SendMail
process.
For information, see
Invoking the SendMail process using the Java Invocation API
.
Invoking the services in the Bank component using web services
You can invoke both the Customer service and Account service (both
services are defined in the Bank component) using web services.
This discussion describes how to create a .NET client application
that invokes both of these services. That is, the .NET application
is able to work with custom data types defined in the Bank component.
The encoding type used in the client project is MTOM. (See
Invoking LiveCycle using MTOM
.)
Because there are two LiveCycle services being invoked,
create two service references in your .NET client project. The first
service reference is associated with the Customer service and the
other service reference is associated with the Account service.
Information about how to create a service reference is described
in
Invoking LiveCycle using MTOM
.
The WSDL to create a service reference to the Customer service
is:
-
http://hiro-xp:8080/soap/services/CustomerService?WSDL&lc_version=9.0.1
Likewise,
the WSDL to create a service reference to the Account service is:
-
http://hiro-xp:8080/soap/services/AccountService?WSDL&lc_version=9.0.1
Note:
Replace
localhost
with the IP
address of the server hosting LiveCycle.
To demonstate
how to work with custom data types using web services, this section
creates a .NET client application that invokes two operations. It creates
a new customer by invoking Customer service’s
createCustomer
operation.
It also invokes the Account service’s
createNewAccount
operation
and creates a new account. The following illustration shows the
client .NET application that invokes the Customer and Account services.
Note:
This application is a #C Windows Forms Application
created in Visual Studio 2008. The name of the client project is
BankComponentClientApplication
.
The
following table lists the controls that are part of this client
application.
Control name
|
Description
|
textBoxFirst
|
Specifies the customer’s first name.
|
textBoxLast
|
Specifies the customer’s last name.
|
textBoxPhone
|
Specifies the customer’s phone number.
|
textBoxStreet
|
Specifies the customer’s street name.
|
textBoxState
|
Specifies the customer’s state.
|
textBoxZIP
|
Specifies the customer’s zip code.
|
textBoxCity
|
Specifies the customer’s city.
|
comboBoxAccountType
|
Specifies the type of account.
|
textBoxCustomerId
|
Specifies the customer identifier value
to which the new account belongs. This text box is populated by
the return value of the Customer service’s
createCustomer
operation.
|
textBoxAccountBalance
|
Specifies the account balance.
|
dateTimePicker1
|
Specifies the date on which the account
was created.
|
textBoxAccountId
|
Specifies the account’s identifier value.
This text box is populated by the return value of the Account service’s
createNewAccount
operation.
|
buttonCreateCustomer
|
The button that creates a new customer.
|
buttonCreateAccount
|
The button that creates a new account.
|
The following C# code example invokes the
Account and Customer services and creates a new customer and a new
account. The code to create a new customer is located in the
buttonCreateCustomer_Click
method.
The code to create a new account is located in the
buttonCreateAccount_Click
method.
Ensure that you fully qualify the service clients as shown in the
following code example.
Invoking the Account and Customer services using a .NET application
???/**
* Ensure that you create a .NET project that uses
* MS Visual Studio 2008 and version 3.5 of the .NET
* framework. This is required to invoke a
* LiveCycle service using MTOM.
*
* For information, see "Invoking LiveCycle using MTOM" in Programming with AEM forms
*/
using System;
using System.ServiceModel;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
//A reference to the Customer Service
using BankComponentClientApplication.ServiceReference1;
//A reference to the Account Service
using BankComponentClientApplication.ServiceReference2;
namespace BankComponentClientApplication
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//This method invokes the Customer service and creates a new customer
private void buttonCreateCustomer_Click(object sender, EventArgs e)
{
//Create a CustomerServiceClient object
BankComponentClientApplication.ServiceReference1.CustomerServiceClient custClient = new BankComponentClientApplication.ServiceReference1.CustomerServiceClient();
custClient.Endpoint.Address = new System.ServiceModel.EndpointAddress("http://hiro-xp:8080/soap/services/CustomerService?blob=mtom");
//Enable BASIC HTTP authentication
BasicHttpBinding b = (BasicHttpBinding)custClient.Endpoint.Binding;
b.MessageEncoding = WSMessageEncoding.Mtom;
custClient.ClientCredentials.UserName.UserName = "administrator";
custClient.ClientCredentials.UserName.Password = "password";
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
b.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
b.MaxReceivedMessageSize = 2000000;
b.MaxBufferSize = 2000000;
b.ReaderQuotas.MaxArrayLength = 2000000;
//Create a new Customer object
Customer myCustomer = new Customer();
//Populate the Customer fields with data entered into the Windows Form
myCustomer.city = textBoxCity.Text;
myCustomer.name = textBoxFirst.Text + " " + textBoxLast.Text;
myCustomer.phone = textBoxPhone.Text;
myCustomer.state = textBoxState.Text;
String myZIP = textBoxZIP.Text;
myCustomer.zip = Convert.ToInt32(myZIP);
myCustomer.street = textBoxStreet.Text;
//Invoke the LiveCycle operation named createCustomer
String custId = custClient.createCustomer(myCustomer);
//Set the Customer Id text box in the New Account Group Box
textBoxCustomerId.Text = custId;
}
//This method invokes the Account service and creates a new Account
private void buttonCreateAccount_Click(object sender, EventArgs e)
{
//Create a CustomerServiceClient object
BankComponentClientApplication.ServiceReference2.CustomerServiceClient accountClient = new BankComponentClientApplication.ServiceReference2.CustomerServiceClient();
accountClient.Endpoint.Address = new System.ServiceModel.EndpointAddress("http://hiro-xp:8080/soap/services/AccountService?blob=mtom");
//Enable BASIC HTTP authentication
BasicHttpBinding b = (BasicHttpBinding)accountClient.Endpoint.Binding;
b.MessageEncoding = WSMessageEncoding.Mtom;
accountClient.ClientCredentials.UserName.UserName = "administrator";
accountClient.ClientCredentials.UserName.Password = "password";
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
b.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
b.MaxReceivedMessageSize = 2000000;
b.MaxBufferSize = 2000000;
b.ReaderQuotas.MaxArrayLength = 2000000;
//Create a BLOB to store the form associated with the account
BLOB inDoc = new BLOB();
//Specify the PDF form
string path = "C:\\Adobe\Loan.pdf";
FileStream fs = new FileStream(path, FileMode.Open);
//Get the length of the file stream
int len = (int)fs.Length;
byte[] ByteArray = new byte[len];
//Populate the byte array with the contents of the FileStream object
fs.Read(ByteArray, 0, len);
inDoc.MTOM = ByteArray;
//Determine the type of Account to create
if (comboBoxAccountType.Text == "Savings")
{
//Create a AccountInfo object
AccountInfo accountInfo = new AccountInfo();
accountInfo.customerId = textBoxCustomerId.Text;
String accountBal = textBoxAccountBalance.Text;
accountInfo.accountBalance = Convert.ToDouble(accountBal);
//Get the Date information
BankComponentClientApplication.ServiceReference2.DATE myDate = new BankComponentClientApplication.ServiceReference2.DATE();
DateTime time1 = Convert.ToDateTime(dateTimePicker1.Text);
myDate.date = time1;
accountInfo.accountOpenedDate = myDate;
accountInfo.newAccountSignatureForm = inDoc;
accountInfo.accountType = "Savings";
//Invoke the LiveCycle operation named createNewAccount
long accountId = accountClient.createNewAccount(AccountTypes.Savings,accountInfo);
//Convert to a string
String accString = Convert.ToString(accountId);
textBoxAccountId.Text = accString;
}
}
private void Form1_Load(object sender, EventArgs e)
{
comboBoxAccountType.Text = "Savings";
}
}
}
|
|
|