Skip Headers
Oracle® Fusion Middleware Developer's Guide for Oracle WebCenter Portal (Oracle Fusion Applications Edition)
11g Release 1 (11.1.1.6.3)

Part Number E25595-02
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

67 Personalizing WebCenter Portal Applications

Personalization for WebCenter Portal delivers targeted content based on both user and application context. WebCenter Portal's Personalization also provides a run-time system and associated tools that allow the declarative definition of application flow.

This chapter describes how to integrate Personalization into a WebCenter Portal: Framework application and includes the following sections:

For more information about Personalization, see:

67.1 Introduction to Personalization

Personalization provides a dynamically derived user experience for your WebCenter Portal application. Personalization evaluates defined sources of input data, generates a decision based on that evaluation, and applies this information to a declaratively defined Personalization scenario. Personalization, for example, can return content or change application flow based on information about a user in a Human Resources database, targeting the application experience for that specific user.

This section provides an overview of the features and requirements of Personalization, and contains the following subsections:

67.1.1 Personalization Architecture

Figure 67-1 shows the architecture and out-of-the box services and components that comprise Personalization. These and other Personalization components are described in:

Figure 67-1 Personalization Services Architecture

Description of Figure 67-1 follows
Description of "Figure 67-1 Personalization Services Architecture"

67.1.2 Personalization Conductor

This section describes the role of the Personalization Conductor. The Conductor is the heart of the Personalization engine, and contains and runs the units of work called scenarios. The Conductor runs the prepared scenarios, and provides access to provider extensions that plug into the Conductor. These aspects of the Conductor are described in the following sections:

67.1.2.1 Personalized Scenarios

A scenario is defined by a script you create using the Scenario Editor in JDeveloper (or provide as an XML file) based on a simple syntax that allows conditional statement evaluation resulting in the return (to the client) of some form of content. Based on function provider expressions, a script action may then return filtered content to the client application using a Data Provider and a query. A scenario, rather than returning targeted content, can also be used to make dynamic changes to application flow, resulting in a personalized user experience within the application.

In JDeveloper, you can use the Scenario Editor to graphically design your scenario. You can also create XML file-based scenarios. See Section 67.2.2.4, "Specifying Scenario Flow Using Node Types" and Section 67.2.3, "Creating File-Based Scenarios" for more information about creating scenarios.

67.1.2.2 Personalized Application Flow

As well as returning content targeted to a specific user, scenarios executing within the Conductor can also control application flow, render user interface components, and execute application-related events. Using the Conductor's public Java and Expression Language (EL) API, you can provide seamless integration with an existing Java or Web-based application. See Chapter 68, "Using Personalization APIs Externally" for more information about using Java and EL APIs.

67.1.2.3 Property Set Integration

The Property Service uses property sets to organize sets of user or application data, and property definitions to assign a data type to property data. You can extend the Property Service to access additional user profile data in an enterprise LDAP repository, or to access additional repositories such as Oracle MDS. For more information about the Property Service and property sets, see Section 67.1.3, "Personalization Property Service."

67.1.2.4 Provider Integration

Providers provide a way to access external resources within your scenario. As well as three out-of-the-box providers for the Property Service, Content Server Provider (CMIS), and Activity Graph, the Conductor also supports an extensible architecture that lets you implement and access custom providers from within your Scenarios. For more information about providers, see Section 67.1.4, "Personalization Providers."

67.1.3 Personalization Property Service

This section describes the Personalization Property Service. The Property Service provides a simple yet scalable way for developers to access user data.

This section includes the following subsections:

67.1.3.1 Property Service Architecture

The Property Service uses Java and REST APIs to store and retrieve properties about a user, such as their age or gender, or other information such as the current time. These properties are part of a property set. A property set can have a schema defined (the schema defines types for properties), or it can be created on demand without a schema. A property set can have properties that are retrieved from different locations such as the Identity Store LDAP server, a file, or using a SQL query. For more information about using Java and REST APIs, see Chapter 68, "Using Personalization APIs Externally"

The Property Service uses the default Personalization cache (or Coherence cache if available) and by default stores property sets in a database. The Property Service is also extensible allowing additional repositories such as Oracle MDS to be configured.

Figure 67-2 Property Service Architecture

Description of Figure 67-2 follows
Description of "Figure 67-2 Property Service Architecture"

67.1.3.2 Property Sets

The Property Service uses property sets to organize sets of user or application data, and property definitions to assign a data type to property data. The existing user profile data in an enterprise LDAP repository can also be made accessible by extending the Property Service. See Section 67.2.2.5, "Defining Property Sets and Property Locators" for more information about the property sets. In a JDeveloper project for a WebCenter Portal application, the PropertySet Editor creates property namespace files containing namespace, PropertySet, and Property definitions.

67.1.3.3 Property Locators

A property locator is invoked by the Property Service to access external stores of properties. For example, Personalization provides an out-of-the-box People Connections property locator that uses the People Connections REST service to access profile information for users. For more information about property locators, see Section 67.2.2.5, "Defining Property Sets and Property Locators." For more information about the People Connections property locator, see Section 67.1.4.1.4, "People Connections Locator."

67.1.4 Personalization Providers

A provider is an interface point for the Conductor that lets it communicate with external services. Personalization provides out-of-the-box providers for the Property Service, Oracle WebCenter Content: Content Server, and Activity Graph, and a locator used by the Property Service provider to allow integration with People Connections. There are also two types of custom providers you can implement: data providers and function providers.

This section describes the out-of-the-box providers and the two types of custom providers in the following subsections:

67.1.4.1 Out-of-the-Box Providers

This section describes the out-of-the-box providers you can access from within your scenarios, and a locator used by the Property Service provider to allow integration with People Connections.

This section contains the following subsections:

67.1.4.1.1 Property Service Provider

The Property Service Provider allows integration with the Property Service. This provider allows you to retrieve property sets for a particular property set definition, retrieve a property set by name, or return a specific property from a property set. For more information about the Property Service Provider, see Section 67.2.2.8.1, "Using the Property Service Provider."

67.1.4.1.2 CMIS Provider

The CMIS Provider provides services to retrieve and search for content from standards-based CMIS (Content Management Interoperability Services) content servers, specifically provided by the Oracle WebCenter Content Server. The CMIS Provider also has utility function providers to convert results to simplified form. For information about integrating the CMIS Provider, see Section 67.2.2.8.2, "Using the CMIS Provider." For information about creating and editing connection configuration for the Content Server provider, see "Configuring the REST Service Identity Asserter" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

67.1.4.1.3 Activity Graph Provider

The Activity Graph Provider (ActivityGraphProvider) provides integration with the Activity Graph engine (the basis for the Activity Graph service), which provides recommendations about what a user may be interested in connecting with based on analytics within WebCenter. For example, the Activity Graph Provider method recommendedUsers, queries the Activity Graph engine for a list of users that a given user might want to connect with.

The Activity Graph engine provides a central repository for actions that are collected by enterprise applications. For more information about the Activity Graph service and the Activity Graph engine, see Section 46, "Integrating the Activity Graph Service."

The Activity Graph provider is built on two REST services for Activity Graph running on the WebCenter Portal: Spaces server, and consequently requires that WebCenter Portal: Spaces be installed in your environment. For more information about integrating the Activity Graph provider in your scenario, see Section 67.2.2.8.3, "Using the Activity Graph Provider." For information about creating and editing connection configuration for the Activity Graph provider, see "Configuring the Activity Graph Provider" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

67.1.4.1.4 People Connections Locator

Although The People Connections locator is an ILocator used by the Property Provider to interface with the WebCenter People Connections service. One of the benefits of the People Connections service is that it can access user profile information stored on WebCenter Portal: Spaces. For more information about the People Connections service, see Part VI, "Working with the People Connections Service". For more information about the People Connections locator, see Section 67.2.2.8.1, "Using the Property Service Provider." For information about creating and editing connection configuration for the People Connections locator, see "Configuring the Oracle People Connections Locator" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

67.1.4.2 Custom Data Providers

A data provider is a component that plugs into the Conductor architecture and acts as a delegate for some service. Data returned from a data provider can be used as-is (Java classes), or chained to be used as input to other data providers in the same scenario.

The Property Service Provider, Activity Graph Provider, and the CMIS Provider are all examples of data providers that are available out-of-the-box. But data providers (oracle.wcps.conductor.provider.IDataProvider) also provide an extension point for the Conductor so that you can write a custom data provider to, for example, integrate with legacy systems or external data stores. You can then call your custom (or out-of-the-box) data provider using the Invoke Provider node from within a scenario.

Data providers typically operate on the input context of a given user or an external application data set (or both). Personalization includes a Property Service that can provide typed data storage for user or application data, and is accessible as input to providers. For more information about creating your own data provider, see Section 67.4.1, "Adding Custom Providers."

67.1.4.3 Function Providers

Function providers (oracle.wcps.conductor.provider.IFunctionProvider) also provide an extension point for the Conductor to create utility methods (for example, for data manipulation or transformation, or business rule calculation) that can be invoked using Expression Language (EL) expressions. A simple example might be: ${strings:concat('string1','string2')}). The CMIS and Activity Graph providers each have their own function provider to facilitate data transformations, among other utilities. For more information about Personalization ELs, see Appendix G, "ELs Related to the Personalization Service."

67.2 Integrating Personalization in Your Application

This section describes how to integrate Personalization in your Framework application.

This section contains the following subsections:

67.2.1 Personalization Requirements

This section describes the design-time system requirements and dependencies for developing WebCenter Portal applications with JDeveloper that integrate Personalization services. For a complete list of requirements, dependencies, and options for Personalization, see "Personalization Prerequisites" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

  • WebCenter Portal: Spaces must be installed if the CMIS Provider, Activity Graph Provider, or People Connections locator are being used in a scenario. Note that for basic iterative development WebCenter Portal: Spaces is not required to be installed on your domain.

  • Before you can use REST APIs, you must take the steps outlined in "Before You Begin: Performing Required Configurations" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

  • Personalization relies on WebCenter Trust Services to provide single sign-on (SSO) between different managed servers within the WebCenter domain, including the Oracle WebCenter Content: Content Server that is accessed through the CMIS Provider. For JDeveloper's integrated domain, the Trust Services must be configured using a WLST script (configureWCPS.py) located in the DefaultDomain/scrpts-wcps directory. For more information about configuring the WebCenter Trust Services and single sign-on using this script see "Configuring Single Sign-on" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

  • The REST service must be configured. For information about how to configure the REST service, see the chapter "Managing REST Services" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

Note:

Although you can optionally include the out-of-the-box providers for Activity Graph and People Connections in your application, you will not be able to test results in JDeveloper. Note also that these providers are only partially configured, and you must complete their configuration as described in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal in the sections on "Configuring the Oracle People Connections Service" and "Configuring the Activity Graph Service."

67.2.2 Authoring Personalized Scenarios in JDeveloper

This section describes how to author personalized scenarios using the Personalization tools in JDeveloper, and includes the following subsections:

67.2.2.1 Creating a Properties Namespace

The Properties Namespace file contains the property definitions and property set definitions you define.

To create a Properties Namespace file in JDeveloper:

  1. From within your application in JDeveloper, right-click the application in the navigation bar.

    The New Gallery dialog displays (see Figure 67-3).

    Figure 67-3 New Gallery Dialog

    Description of Figure 67-3 follows
    Description of "Figure 67-3 New Gallery Dialog"

    Note:

    If the Personalization category doesn't display, open the All Technologies tab and select it, or click selected technologies and add the Personalization technology to the project.

  2. Choose Properties Namespace and click OK.

    The Create Namespace dialog displays (see Figure 67-4).

    Figure 67-4 Create Namespace Dialog

    Description of Figure 67-4 follows
    Description of "Figure 67-4 Create Namespace Dialog"

  3. Enter a name for the namespace, select the Include default property definitions checkbox to add initial default property definitions to the namespace, and click OK.

    The Include default property definitions checkbox controls whether initial default property definitions are added to the namespace file. These are: IntDef, NumberDef, StringDef, BooleanDef, DateTimeDef, ClobDef, IntArrayDef, NumberArrayDef, StringArrayDef, BooleanArrayDef, DateTimeArrayDef, ClobArrayDef.The property definitions can be removed, if needed afterwards. Or, you can leave the checkbox unselected to start with no initial property definitions and create them as needed in the Scenario Editor.

    A graphic representation of the namespace file displays in the Design pane (see Figure 67-5).

    Figure 67-5 Design Pane - Namespace Definition

    Description of Figure 67-5 follows
    Description of "Figure 67-5 Design Pane - Namespace Definition"

  4. Continue by defining the property sets and properties that you will need in your scenario as described in Section 67.2.2.2, "Creating Property Sets and Property Definitions."

67.2.2.2 Creating Property Sets and Property Definitions

Properties are the bits of data about someone or something on which, for example, you can base evaluations of conditionals in your scenario's flow. A property, for example, could contain a person's age or gender. A property set is simply a collection or container of related properties.

To define a property set and associated properties:

  1. From within your application in JDeveloper, right-click the namespace in the Design pane, select Create New Property Set Definition, and click OK.

    The Create New Property Set dialog displays (see Figure 67-6).

    Figure 67-6 Create New Property Set Dialog

    Description of Figure 67-6 follows
    Description of "Figure 67-6 Create New Property Set Dialog"

  2. Enter a name for the property set and click OK.

    A node for the new property set displays in the Design pane (see Figure 67-7).

    Figure 67-7 Design Pane - New Property Set Definition

    Description of Figure 67-7 follows
    Description of "Figure 67-7 Design Pane - New Property Set Definition"

  3. To add properties to the new property set, right-click the property set, and select Create New Property Definition Mapping.

    The Create Property Definition dialog displays (see Figure 67-8).

    Figure 67-8 Create Property Definition

    Description of Figure 67-8 follows
    Description of "Figure 67-8 Create Property Definition"

  4. Enter a Property Name and Definition (the value type), and click OK.

    To create a new property definition, select New Definition in the Definition drop down list.

    The new property appears as part of the property set's graphic representation (see Figure 67-9).

    Figure 67-9 Design Pane - Property Definition

    Description of Figure 67-9 follows
    Description of "Figure 67-9 Design Pane - Property Definition"

  5. Add any additional properties required to the property set (or create new property sets) and then continue by creating the scenario as described in Section 67.2.2.3, "Creating a New Scenario in the Scenario Editor."

67.2.2.3 Creating a New Scenario in the Scenario Editor

The Conductor's Scenario Editor provides a visual tool for viewing and editing scenario definitions managed by the Conductor. The Scenario Editor stores scenarios in both file and visual formats and is available from within JDeveloper. Before creating a scenario, you should have created a namespace and at least some of the property sets and properties you need for the scenario.

To create a new scenario using the Scenario Editor in JDeveloper:

  1. From within your application in JDeveloper, right-click the application in the navigation bar.

    The New Gallery dialog displays (see Figure 67-10).

    Figure 67-10 New Gallery Dialog

    Description of Figure 67-10 follows
    Description of "Figure 67-10 New Gallery Dialog"

    Note:

    If the Personalization category doesn't display, open the All Technologies tab and select it, or click selected technologies and add the Personalization technology to the project.

  2. Select Conductor Scenario and click OK.

    The Scenario Definition dialog displays (see Figure 67-11).

    Figure 67-11 Scenario Definition Dialog

    Description of Figure 67-11 follows
    Description of "Figure 67-11 Scenario Definition Dialog"

  3. Select Create a new conductor scenario file and modify the file name and where the file is stored if necessary. Note that the scenario name's extension (.scenarios_diagram) cannot be changed.

    The Scenario Editor displays with a new Start node (see Figure 67-12).

    Figure 67-12 Scenario Editor - Start Node

    Description of Figure 67-12 follows
    Description of "Figure 67-12 Scenario Editor - Start Node"

  4. To add nodes to the scenario, right-click the node to which you want to add a new node, select Add Following Statement, and chose the node type from the list as shown in Figure 67-13.

    Figure 67-13 Adding a Node in the Scenario Editor

    Description of Figure 67-13 follows
    Description of "Figure 67-13 Adding a Node in the Scenario Editor"

    Right-click the new node to expose options for setting node properties and extending the flow. For a description of available node types, see Section 67.2.2.4, "Specifying Scenario Flow Using Node Types."

67.2.2.4 Specifying Scenario Flow Using Node Types

The Scenario Editor uses a tree-structured fixed layout where the flow is represented by a combination of subtrees (for decisions) and internal label references to represent flow for loops and complex nested if/else decisions.

Node Types

  • Start: The Start node is a root level node that defines where a scenario begins.

  • Return: The Return node halts execution of the scenario, and returns the specified results of evaluation of an EL expression to the caller.

  • Variable Assignment: The variable assignment node lets you define a variable that is scoped within the context of the currently running scenario and initialize it with an expression.

  • Execute: The Execute node invokes a specified EL expression with no expected return value. Similar to Variable Assignment, only the results of the expression are not stored within the Scenario context.

  • Condition/Otherwise: The Condition node evaluates an EL expression and executes the contained statements if the EL expression evaluate to true. Statements within the Otherwise block will only execute if all condition statements evaluate to false.

  • For Each: For Each nodes are a matched pair of nodes that provide looping/iterating functionality over a collection of things.

  • While: Provides looping/iterating functionality as long as a specified EL expression evaluates to true.

  • Raise Error: Raise and throw an error with the specified error message

  • Invoke Provider: Invokes a named implementation of IDataProvider. Results are stored within the context of the currently running Scenario.

  • Invoke Scenario: Invokes another named Scenario within the same namespace. Results are stored within the context of the currently running Scenario

67.2.2.5 Defining Property Sets and Property Locators

The Property Editor lets you view and edit the property set definitions and their associated property definitions managed by the Property Service. The Property Editor communicates and persists data through the Property Service.

Property sets and properties within a property set can be stored in many different locations and formats, so a locator facility is needed to retrieve them. A default property set locator is provided that retrieves and stores property set data to a pre-configured database. This default locator is used when no other is specified for the property set.

A custom locator for the People Connections service is also provided out-of-the-box (for more information, see Section 67.1.4.1.4, "People Connections Locator"), but you may want to define additional locators for other data sources. You can define multiple locators for properties and property sets, and they can also be defined in order of precedence. For step-by-step instructions for how to implement a locator see Section 67.4.2, "Adding Custom Locators."

67.2.2.6 Implementing and Calling Function Providers

The IFunctionProvider is a marker interface that allows utility code to be accessible from the Expression Language services within the Conductor. Implementations of IFunctionProvider contain annotated public and static methods. Example 67-1 shows an example function provider:

Example 67-1 Example Function Provider

@FunctionProvider
(
    prefix="interpreter",
    resourceBundle="sample.InterpreterResources",
    nameBundleKey="provider.name",
    descriptionBundleKey="provider.description"
)
public class InterpreterProvider
implements IFunctionProvider
{
 
    @PublicFunction
    (
        functionName="english",
        descriptionBundleKey="english.description"
    )
    public static String
        interpretEnglish(@PublicParameter(parameterName="phrase",
                                     descriptionBundleKey="phrase.description")String phrase)
    {
        String result = "Not Understood";
 
        if ("Bonjour".equals(phrase))
        {
           result = "Hello";
        }
 
        return result;
    }
 
    @PublicFunction
    (
        functionName="french",
        descriptionBundleKey="french.description"
    )
    public static String
        interpretFrench(@PublicParameter(parameterName="phrase",
                                     descriptionBundleKey="phrase.description")String phrase)
    {
        String result = "Non compris";
 
        if ("Hello".equals(phrase))
        {
           result = "Bonjour";
        }
 
        return result;
    }
}

You would call the function provider in Example 67-1 using the following expression syntax:

${interpreter:english("Bonjour")}
${interpreter:french("Hello")}

Here is an example of how to assign a variable to the results of a function provider based on Example 67-1:

<assign-variable>
     <variable>greetings</variable>
    <expression>${interpreter:english("Bonjour")}</expression>
</assign-variable>

67.2.2.7 Implementing and Calling Data Providers

Data provider implementations are a more structured way of integrating custom functionality with scenario execution. Unlike function providers, data providers have access to a connection configuration framework built into the Conductor so that environment-specific configuration data can be used. The following diagram illustrates the class interaction for data providers:

Figure 67-14 Class Interaction for Data Providers

Description of Figure 67-14 follows
Description of "Figure 67-14 Class Interaction for Data Providers"

IDataProvider

Data providers are responsible for returning abstract named connection implementations of IConnection. These named connections are usually backed by environmental configurations provided by the Conductor's connection configuration service, but do not have to be. Should the data provider happen to use the configuration service, the implementation must return a class that extends oracle.wcps.connection.configuration.AnnotatedConfiguration from the getConnectionConfigClass() method. This class must contain public annotated fields that represent configuration data specific to the data provider.

IConnection

Implementations of oracle.wcps.conductor.provider.IConnection are responsible for returning (named and default) Executable Resources. Each instance of IConnection can represent a single connection configuration and should be constructed within the instance of IDataProvider (not static).

IExecutableResource

Implementations of oracle.wcps.conductor.provider.IExecutableResource contain most of the integration code with the external system and the instance should be constructed within the instance of IConnection (not static). The default method from within IExecutableResource is the execute method that takes no arguments, but custom annotated methods can be written within the implementation if IExecutableResource and exposed to scenario execution. For example, the following custom method can be invoked from a scenario:

@PublicFunction
            (
                    functionName="custom",
                    descriptionBundleKey="customFunction.description"
            )
    public String callCustomMethod(@PublicParameter(parameterName="customParam",
            descriptionBundleKey="customParam.description") String param)
    {
        return param + " Results";
    }

Here is how the provider with the custom method is invoked from within the scenario:

<call-provider>
            <provider-name>myCustomProvider</provider-name>
            <connection-name>${connectionName}</connection-name>
            <method-name>custom</method-name>
            <variable>searchResults</variable>
            <input>
                <parameter>
                    <common:name>customParam</common:name>
                    <common:value>${someScenarioVariable}</common:value>
                </parameter>
            </input>
        </call-provider>

67.2.2.8 Using the Out-of-the-Box Providers

Personalization provides three providers out-of-the-box providers for use within scenarios: the Property Service Provider, the Content Server Provider (CMIS), and Activity Graph. This section describes how you can integrate these providers in your application in the following subsections:

67.2.2.8.1 Using the Property Service Provider

The Property Service Provider (oracle.PropertiesServiceProvider) allows integration with the Personalization Server Property Service. This provider lets you to retrieve property sets for a particular property set definition, retrieve a property set by name, or return a specific property from a property set. If you are using the People Connections locator to integrate profile data into your scenario, then you must configure the Property Service before using it. For more information about the People Connections locator, see Section 67.1.4.1.4, "People Connections Locator." For information about configuring the Property Service provider, see "Configuring the Oracle People Connections Locator" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

67.2.2.8.2 Using the CMIS Provider

The CMIS provider (CMISProvider) provides services to search for and retrieve content from standards-based CMIS (Content Management Interoperability Services) content servers. For WebCenter, this is specifically provided by the Oracle WebCenter Content: Content Server. The CMIS provider also has a utility function provider (CMISFunctionProvider) to convert results to simplified form. Before trying the examples below, be sure to configure your CMIS provider connection as shown in the section on "Configuring the CMIS Provider" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

Example CMIS Provider Scenario

Create a scenario using the following (remember to set the content-type of application/xml):

Request URI:

POST http://localhost:8891/wcps/api/conductor/namespaces/myNameSpace/scenarios

Request Body:

<scenarioMetadata>
<scenario xmlns:common="http://xmlns.oracle.com/wcps/conductor/common/1.0.0" xmlns:scenario="http://xmlns.oracle.com/wcps/conductor/scenarios/1.0.0">
  <body>
    <call-provider>
      <provider-name>CMISProvider</provider-name>
      <connection-name>CmisConfigConnection</connection-name>
      <variable>exprString</variable>
      <input>
        <parameter>
            <common:name>search</common:name>
            <common:value>SELECT * FROM cmis:document WHERE cmis:name LIKE 'flowers'</common:value>
        </parameter>
      </input>
    </call-provider>
    <return>
      <expression>${exprString}</expression>
    </return>
  </body>
  <name>CMIS Scenario</name>
  <comments>CMIS Scenario comments</comments>
  <tags>
    <tag>CMIS Scenario tag</tag>
  </tags>
</scenario>
</scenarioMetadata>

Executing the Scenario

Request URI:

POST http://localhost:8891/wcps/api/conductor/namespaces/myNameSpace/scenarios/CMIS%20Scenario

Request Body:

<parameters/>

Using the CMISFunctionProvider

A function provider is a utility class that can be invoked from within a scenario. The CMISFunctionProvider supports several methods (see the JavaDoc for the class for more information). Here are two example methods:

/**
     * Convert the array of input IDs to a CMIS query in the form of 'IN'
     * @param ids Array of document IDs, in the form of dDocName only
     * @return CMIS query String
     */
public static String getCMISQueryForDocIDs(
    @PublicParameter(parameterName="repository",descriptionBundleKey="getCMISQueryForDocIDs.parameter.description.inputList") String repository,
    @PublicParameter(parameterName="ids",descriptionBundleKey="getCMISQueryForDocIDs.parameter.description.inputList") List<String> ids))
...


/**
     * Convert the array of input IDs to a CMIS query in the form of 'IN'
     * @param ids, in the form of a WebCenter document ObjectURN.  For example: stanl18-ucm11g#dDocName:MOUNTAINS
     * @return CMIS query String
     */
 public static String
    getCMISQueryForDocIDsFromFullID(@PublicParameter(parameterName="ids",
            descriptionBundleKey="getCMISQueryForDocIDs.parameter.description.inputList") List<String> ids)

Using the CMISFunctionProvider in a Scenario

The following scenario snippet shows how data might be fed into the CMISFunctionProvider to create a CMIS query for multiple nodes, where the query is then sent to the CMIS provider. (In a more realistic case, rather than generate the data, we could have passed the results from an Activity Graph provider call, for example.)

<scenario:scenario xmlns:common="http://xmlns.oracle.com/wcps/conductor/common/1.0.0"
                   xmlns:scenario="http://xmlns.oracle.com/wcps/conductor/scenarios/1.0.0">
  <comments>testFunctionProviderScenario4 comments</comments>
  <body>
   <assign-variable>
      <variable>ids</variable>
      <expression>${collections:new()}</expression>
    </assign-variable>
    <execute>
      <expression>${collections:append(ids,'StellentRepository#dDocName:MOUNTAINS')}</expression>
    </execute>
    <execute>
      <expression>${collections:append(ids,'StellentRepository#dDocName:PARKS')}</expression>
    </execute>
    <execute>
      <expression>${collections:append(ids,'StellentRepository#dDocName:PATAGONIAPARKS')}</expression>
    </execute>
 
    <!-- Assign those as IDs to send to CMIS -->
    <assign-variable>
        <variable>cmisQuery</variable>
        <expression>${cmisfunction:getCMISQueryForDocIDsFromFullID(ids)}</expression>
    </assign-variable>
 
    <!-- Make the call to CMIS -->
    <call-provider>
      <provider-name>CMISProvider</provider-name>
      <connection-name>NonProxyConnection</connection-name>
      <variable>cmisResults</variable>
      <input>
        <parameter>
            <common:name>search</common:name>
            <common:value>${cmisQuery}</common:value>
        </parameter>
      </input>
    </call-provider>
    <return>
      <expression>${cmisResults}</expression>
    </return>
  </body>
  <name>testFunctionProviderScenario4</name>
 
</scenario:scenario>
67.2.2.8.3 Using the Activity Graph Provider

The Activity Graph Provider (ActivityGraphProvider) provides integration with the Activity Graph service, which provides recommendations based on analytics within WebCenter. Before attempting the examples below, be sure to configure the connection for the Activity Graph service as shown in the section on "Configuring the Activity Graph Provider" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter Portal.

Example Activity Graph Provider Scenario

Create a scenario using the following (remember to set the content-type of application/xml):

Request URI:

POST http://localhost:8891/wcps/api/conductor/namespaces/default/scenarios

Request Body:

<scenarioMetadata>
<scenario xmlns:common="http://xmlns.oracle.com/wcps/conductor/common/1.0.0">
    <body>
        <call-provider>
            <provider-name>ActivityGraphProvider</provider-name>
            <connection-name>ActivityGraphConfigConnection</connection-name>
            <resource-name>QueryCommonItems</resource-name>
            <method-name>queryCommonItems</method-name>
            <variable>providerResults</variable>
            <input>
                <parameter>
                    <common:name>sourceClassURN</common:name>
                    <common:value>WC.user</common:value>
                </parameter>
                <parameter>
                    <common:name>sourceObjectURN</common:name>
                    <common:value>carl</common:value>
                </parameter>
                <parameter>
                    <common:name>targetClassURN</common:name>
                    <common:value>WC.user</common:value>
                </parameter>
                <parameter>
                    <common:name>targetObjectURN</common:name>
                    <common:value>monty</common:value>
                </parameter>
                <parameter>
                    <common:name>similarityURN</common:name>
                    <common:value>user-edit</common:value>
                </parameter>
            </input>
        </call-provider>
        <return>
            <expression>${providerResults}</expression>
        </return>
    </body>
    <name>QueryCommonItemsScenario</name>
    <comments>QueryCommonItemsScenario comments</comments>
    <tags>
        <tag>invoke</tag>
        <tag>sample</tag>
        <tag>provider</tag>
        <tag>resource</tag>
        <tag>input</tag>
        <tag>parameters</tag>
        <tag>overloaded</tag>
    </tags>
</scenario>
</scenarioMetadata>

Executing the Scenario

Before continuing, you must set the header values: Accept=application/xml and content-type=application/xml.

Request URI:

POST http://localhost:8891/wcps/api/conductor/namespaces/default/scenarios/QueryCommonItemsScenario

Request Body:

<parameters/>

Using the AGFunctionProvider

A function provider is a utility class that can be invoked from within a scenario. The AGFunctionProvider supports these methods:

/**
     * Return the value of 'ObjectURN', only for WC.document, from the input Object.
     * @param agResults From call to 'QueryRecommendations' or QueryItems.  Can be one of the following Classes:
     *
     * From AG REST 'recommendations'
     * <ul>
     *   <li> Recommendations
     *   <li> RecommendedItems
     *   <li> List<Recommendation>
     * </ul>
     *
     * From AG REST 'items'   (common items)
     * <ul>
     *   <li> Results
     *   <li> Items
     *   <li> List<Item>
     * </ul>
     *
     * @param dDocNameOnly  If true, just the last part of the ObjectURN for
     * document will be returned; otherwise, the entire ObjectURN will be.
     * For example, if ObjectURN is example-ucm11g#dDocName:EXAMPLEACL000116,
     * if this value is 'true', then only EXAMPLEACL000116 will be returned.
     * Otherwise, all of stanl18-ucm11g#dDocName:EXAMPLEACL000116 is returned.
     *
     * @return a List of ObjectURN representing the document ID
     */
    @PublicFunction
 
    public static List<String> getDocumentIDs(
@PublicParameter(parameterName="agResults",descriptionBundleKey="getContentIDs.parameter.description.inputList")Object agResults,
 @PublicParameter(parameterName="dDocNameOnly",  descriptionBundleKey="getContentIDs.parameter.description.inputList")boolean dDocNameOnly)
    {
...

/**
     * Return the value of 'ObjectURN' for the 'ClassURN' from the input Object.
     * @param agResults From call to 'QueryRecommendations' or QueryItems.  Can be one of the following Classes:
     *
     * From AG REST 'recommendations'
     * <ul>
     *   <li> Recommendations
     *   <li> RecommendedItems
     *   <li> List<Recommendation>
     * </ul>
     *
     * From AG REST 'items'   (common items)
     * <ul>
     *   <li> Results
     *   <li> Items
     *   <li> List<Item>
     * </ul>
     *
     * param filterClassURN return content only of this type.  Examples are: WC.wiki-page, WC.group-space, WC.user, WC.blog,
     * WC.topic, WC.document.  If this is null, return all types.
     *
     * @return a List of ObjectURN representing the item identifier.
     */
    @PublicFunction
            (
                    functionName="getContentIDs",
                    descriptionBundleKey="getContentIDs.method.description"
            )
    public static List<String> getContentIDs(@PublicParameter(parameterName="agResults",
         descriptionBundleKey="getContentIDs.parameter.description.inputList")Object agResults,
                                             @PublicParameter(parameterName="filterClassURN",
                                                     descriptionBundleKey="getContentIDs.parameter.description.filterClassURN") String filterClassURN)
    ...

/**
     * Filter recommendations by score.  Only those recommendations with a score >= the cutoff will be preserved.
     */
    @PublicFunction
            (
                    functionName="filterRecsByScore",
                    descriptionBundleKey="filterRecsByScore.method.description"
            )
    public static Recommendations filterRecsByScore(
            @PublicParameter(parameterName="recommendations",
            descriptionBundleKey="filterRecsByScore.parameter.description.agRecs") Recommendations recs,
            @PublicParameter(parameterName="cutoffScore",
            descriptionBundleKey="filterRecsByScore.parameter.description.cutoffScore") float cutoffScore
            )
    {

Using the AGFunctionProvider in a Scenario

In the following scenario, a call is first made to the Activity Graph REST service. The results of that call are in the agRecommendations variable. The next call in the scenario is to the AGFunctionProvider.getContentIDs() method, passing in the results of the agRecommendations (an Activity Graph object), and returning a List<String> of document IDs, held in the evaluation of the expressions call.

<expression>${agfunction:getContentIDs(agRecommendations,'WC.user')}</expression>

 <scenario:scenario xmlns:common="http://xmlns.oracle.com/wcps/conductor/common/1.0.0"
                   xmlns:scenario="http://xmlns.oracle.com/wcps/conductor/scenarios/1.0.0">
    <comments>CallProviderScenario comments</comments>
    <body>
        <call-provider>
            <provider-name>ActivityGraphProvider</provider-name>
            <connection-name>AgTestServerConnection</connection-name>
            <resource-name>QueryRecommendations</resource-name>
            <method-name>getRecommendationsUsingDefaultRecipe</method-name>
            <variable>agRecommendations</variable>
            <input>
                <parameter>
                    <common:name>classURN</common:name>
                    <common:value>WC.user</common:value>
                </parameter>
                <parameter>
                    <common:name>objectURN</common:name>
                    <common:value>carl</common:value>
                </parameter>
            </input>
        </call-provider>
 
          <!-- Now call function provider to translate into user IDs.  A more specific call is available for WC.document; this just tests
         the ability of the function provider to handle different types of content  -->
        <return>
            <expression>${agfunction:getContentIDs(agRecommendations,'WC.user')}</expression>
        </return>
    </body>
    <name>CallProviderScenario</name>
</scenario:scenario>

67.2.3 Creating File-Based Scenarios

As well as using the Scenario Editor, you can also create file-based scenarios in XML format. Note that file-based scenarios are only supported through the Conductor's import/export function and JDeveloper MDS integration. To import a file-based scenario into the Scenario Editor, select the Create from file option when you create the scenario.

Table 67-1 shows the statements that make up the contents of a scenario file. For more detailed information about the statements, refer to the node descriptions in Section 67.2.2.4, "Specifying Scenario Flow Using Node Types."

Table 67-1 Scenario Statements

Statement Description Example

Variable Assignment

Creates a variable that is scoped within the context of the currently running scenario.

<assign-variable>
     <variable>index</variable>
    <expression>3</expression>
</assign-variable> 

Execute

Invokes a specified EL Expression with no expected return value. Similar to Variable Assignment, only results of the expression are not stored within the Scenario Context.

<execute>
      <comments>Adds to an existing collection.</comments>
      <expression>${collections:append(responses,'The outlook is not so good.')}</expression>
</execute> 

Condition/Otherwise

Evaluates an EL expression and executes the contained statements should the EL expression evaluate to true. Statements within the otherwise block will only execute if all condition statements evaluate to false.

<conditions>
      <body>
        <condition>
          <body>
            <assign-variable>
              <variable>customerType</variable>
              <expression>Great Customer</expression>
            </assign-variable>
          </body>
          <expression>${customer.totalMoneySpent >= 100000}</expression>
        </condition>
        <otherwise>
          <body>
            <assign-variable>
              <variable>customerType</variable>
              <expression>Good Customer</expression>
            </assign-variable>
          </body>
        </otherwise>
      </body>
    </conditions> 

ForEach

Provides looping/iterating functionality over a collection of things.

<for-each>
      <body>
         <execute>
           <comments>
               Let's send each great customer an email, thanking them for their loyalty.
           </comments>
           <expression>
               ${emailService:sendMail(customer,'Subject:Thanks for being a great customer')}
           </expression>
         </execute>
      </body>
      <variable>customer</variable>
      <max-iterations>10</max-iterations>
      <items>${greatCustomers}</items>
    </for-each> 

While

Provides looping/iterating functionality as long as the specified EL expression evaluates to true.

<while>
      <body>
        <assign-variable>
          <comments>Sum up customer invoices to arrive at customer total expenditure.</comments>
          <variable>customerTotalSpent</variable>
          <expression>${customerTotalSpent+order.invoiceTotal}</expression>
        </assign-variable>
      </body>
      <expression>${customer.id = currentCustomerId }</expression>
      <max-iterations>-1</max-iterations>
</while> 

Invoke Scenario

Invokes another named scenario within the same namespace. Results are stored within the context of the currently running scenario.

<call-scenario >
    <variable>greeting</variable>
    <scenario>randomGreetingGenerator</scenario>
    <input>
        <parameter>
          <ns2:name>greetingsList</ns2:name>
          <ns2:value>hello, Bonjour, Buenos días</ns2:value>
        </parameter>
    </input>
</call-scenario> 

Invoke Provider

Invokes a named implementation of IDataProvider. Results are stored within the context of the currently running scenario

<call-provider>
      <provider-name>CRM_Provider</provider-name>
      <connection-name>crm_db</connection-name>
      <resource-name>getGreatCustomers</resource-name>
      <variable>greatCustomers</variable>
      <input>
          <parameter>
              <ns2:name>minMoneySpent</ns2:name>
              <ns2:value>100000</ns2:value>
          </parameter>
      </input>
</call-provider> 

Return

Returns the specified results of evaluation of an EL Expression.

<return>
    <comments>Return the sales rep who sold the most widgets this month.</comments>
    <expression>${bestSalesRep.id}</expression>
</return> 

Raise Error

Raise and throw an error with the specified error message

<raise-error>
      <comments>User is unathorized, so let's return a 401.</comments>
      <statusCode>401</statusCode>
      <message>User ${ScenarioContext.scenarioRequest.userPrincipal.name} is unauthorized to execute this scenario.</message>
</raise-error>

Example 67-2 Example File-based Scenario

<scenario:scenario xmlns:common="http://xmlns.oracle.com/wcps/conductor/common/1.0.0"
                   xmlns:scenario="http://xmlns.oracle.com/wcps/conductor/scenarios/1.0.0">
  <comments>sampleScenarioWithIf comments</comments>
  <body>
    <assign-variable>
      <variable>index</variable>
      <expression>3</expression>
    </assign-variable>
    <conditions>
      <body>
        <condition>
          <body>
            <assign-variable>
              <variable>ResultVar</variable>
              <expression>conditionSatisfied</expression>
            </assign-variable>
          </body>
          <expression>${index == 3}</expression>
        </condition>
        <otherwise>
          <body>
            <assign-variable>
              <variable>ResultVar</variable>
              <expression>otherwiseInvoked</expression>
            </assign-variable>
          </body>
        </otherwise>
      </body>
    </conditions>
    <return>
      <expression>${ResultVar}</expression>
    </return>
  </body>
  <name>sampleScenarioWithIf</name>
  <tags>
    <tag>if</tag>
    <tag>condition</tag>
    <tag>otherwise</tag>
  </tags>
</scenario:scenario>

67.2.4 Displaying Targeted Content at Runtime

This section describes how targeted content is displayed using the Content Presenter to the user at runtime for WebCenter Portal: Spaces and WebCenter Portal: Framework applications. It also describes how you can use REST APIs to create your own user interface for presenting content.

This section contains the following subsections:

67.2.4.1 Presenting Content with Content Presenter

Content Presenter is tightly integrated with the Conductor's dynamic content query generation and enables simple surfacing of dynamic content. For more information about using Content Presenter in your application, see Chapter 29, "Creating Content Presenter Display Templates."

For information about using Expression Language expressions in WebCenter Portal: Spaces to access a Personalization server scenario, see "Personalizing Pages" in the Oracle Fusion Middleware User's Guide for Oracle WebCenter Portal: Spaces.

67.2.4.2 Presenting Content with a Custom User Interface

Using REST APIs, you can create your own user interface for presenting content rather than using Content Presenter. For more information about REST APIs, see Chapter 54, "Using Oracle WebCenter Portal REST APIs."

67.3 Tutorial: Creating, Testing and Deploying a Simple Application

This section describes how to create a simple application in JDeveloper containing a scenario that returns a simple string ("Hello World"). You'll then test the results of that scenario by adding it to a JSP page, and run the application in the integrated WebLogic server to display the results in a browser.

This section also covers the steps to configure and start the integrated WebLogic server, define a connection to the Personalization Server Conductor, and how to deploy the scenario and JSP artifacts to the server domain for testing.

Note:

as a prerequisite to to this tutorial, you should already have installed WebCenter Portal's extension for Oracle JDeveloper (oracle.webcenter.framework_bundle.zip). If you have not already added this extension, use the Update Wizard in JDeveloper to check for updates by clicking Check for Updates from the Help menu.

  1. Create a simple WebCenter Portal application (Hello_Applicaton) using the default options and technologies.

    1. From the File menu select New...

      The New Gallery appears.

    2. From the All Technologies tab, select Applications.

    3. Select Fusion Web Application (ADF) from the list and click OK.

      The Create Fusion Web Application (ADF) wizard appears (see Figure 67-15).

      Figure 67-15 Creating the Hello_Application - Step 1

      Description of Figure 67-15 follows
      Description of "Figure 67-15 Creating the Hello_Application - Step 1"

    4. Enter the Application Name as Hello_Application.

    5. Click Next until you arrive at Step 4 of 5, the wizard page for the Portal project.

      The Project Technologies tab displays a list of available technologies to install for the application.

      Figure 67-16 Creating the Hello_Application - Step 4

      Description of Figure 67-16 follows
      Description of "Figure 67-16 Creating the Hello_Application - Step 4"

    6. Add Webcenter Personalization to the list of selected technologies for the Portal project, and click Finish to create the configured Hello_Application application.

  2. Configure JDeveloper's integrated server's (integrated WLS) default domain.

    The domain will automatically be extended to include necessary server artifacts required for running the Personalization components (i.e., the Personalization Conductor and the Property Service).

    1. From the View menu, select Application Server Navigator and expand the Application Servers tree node to display the IntegratedWeblogicServer.

      The current status should be (domain unconfigured).

    2. Right-click IntegratedWeblogicServer to display the context menu and select Create Default Domain...

      The status displays (domain building) and the Message Log window displays the output as the domain is being built. Wait for the domain to finish building. This may take some time to complete. When complete, the message log should display "Integrated Weblogic domain processing completed successfully."

      Figure 67-17 Application Server Navigator - Creating the Default Domain

      Description of Figure 67-17 follows
      Description of "Figure 67-17 Application Server Navigator - Creating the Default Domain"

    3. Start the server by right-clicking IntegratedWeblogicServer to display the context menu, and selecting Start Server Instance.

      The Running: IntegratedWeblogicServer - Log view should appear and display output as the server is starting. Wait for the server to finish starting. This may take some time to complete. When complete, the IntegratedWeblogicServer Message Log should display IntegratedWebLogicServer started.

      Figure 67-18 Application Server Navigator - Starting the Server

      Description of Figure 67-18 follows
      Description of "Figure 67-18 Application Server Navigator - Starting the Server"

  3. Configure the Personalization (WCPS) Trust service on the running server to enable single sign-on (SSO) for JDeveloper's IntegratedWebLogicServer domain.

    1. From the Tools menu, select Configure WCPS Trust Service.

      The external tool to perform this task will begin running and its output will be directed to the Configure WCPS Trust Service - Log view. Wait for the configuration to complete. This may take a while. When complete, the log will display the message "YOU HAVE MADE CHANGES THAT REQUIRE THE DOMAIN TO BE RESTARTED. PLEASE RESTART ALL DOMAIN SERVERS."

      Figure 67-19 Configuring the WCPS Trust Service

      Description of Figure 67-19 follows
      Description of "Figure 67-19 Configuring the WCPS Trust Service"

    2. Stop the application server by clicking Terminate on the Running: IntegratedWeblogicServer - Log view.

      When shutdown is complete you will see the message "Process Exited". You can then proceed to restart the server as shown in step 2c.

  4. Configure an application URL connection to the Personalization Conductor component of the application server.

    1. From the Application menu select Application Properties...

      The Application Properties dialog displays.

      Figure 67-20 Application Properties Dialog - Configuring the Personalization Server Connection

      Description of Figure 67-20 follows
      Description of "Figure 67-20 Application Properties Dialog - Configuring the Personalization Server Connection"

    2. Select the Personalization Server category, and from the URL Connection: drop-down list and select < New URL Connection... >.

      The Create URL Connection dialog displays (see Figure 67-21).

      Figure 67-21 Create URL Connection Dialog - Conductor URL Connection

      Description of Figure 67-21 follows
      Description of "Figure 67-21 Create URL Connection Dialog - Conductor URL Connection"

    3. Enter the (default) data entry fields as shown below to create the connection.

      Field Name Value

      Name

      Conductor

      URL Endpoint

      http://localhost:7101/wcps/api/conductor/resourceIndex

      Authentication Type

      Basic

      Username

      weblogic

      Password

      weblogic1

      Realm

      WCPS


    4. Click Test Connection.

      The Status should display as "The connections successfully established."

    5. Click OK to create the Conductor URL connection.

    6. On the Personalization Server page, leave the default Scenario Namespace set to the application name (Hello_Application), and click OK in the Application Properties dialog to accept the settings.

  5. Configure the server deployment descriptor for the Hello_Application such that the Personalization Services (WCPS) libraries are available to the application.

    1. In the Application Navigator, expand the folder Portal/Web Content/WEB-INF.

    2. Double-click the weblogic.xml deployment descriptor to open it.

      The WebLogic Descriptor Editor displays.

    3. Select the Libraries category.

    4. Click the Plus (+) icon to create a new library entry.

    5. In the Library Name column of the table enter the following library name:

      wcps-services-client-web-lib
      

      Figure 67-22 WebLogic Descriptor Editor - Adding the WCPS Library

      Description of Figure 67-22 follows
      Description of "Figure 67-22 WebLogic Descriptor Editor - Adding the WCPS Library"

    6. When finished, select Save from the File menu to save the weblogic.xml descriptor.

  6. Create a simple hello_world scenario that returns the string "Hello World" when invoked by the Personalization Conductor.

    1. Right-click the Portal project in the Application Navigator and select New...

      The New Gallery appears.

    2. From the Current Project Technologies tab, select the Personalization category.

    3. Select Conductor Scenario and click OK.

      The New Conductor Scenario dialog displays.

      Figure 67-23 New Conductor Scenario Dialog

      Description of Figure 67-23 follows
      Description of "Figure 67-23 New Conductor Scenario Dialog"

    4. Leave the option Create a new conductor scenario file selected, enter a New scenario filename called hello_world.scenarios_diagram, and click OK to create the scenario.

    5. In the Scenario Editor's Diagram view, right-click the Start node and select Add Following Statement->Return.

      A Return node is added and connected.

    6. Right-click the Return node and select Set Expression...

      The Scenario Expression builder displays (see Figure 67-24).

      Figure 67-24 Scenario Expression Builder

      Description of Figure 67-24 follows
      Description of "Figure 67-24 Scenario Expression Builder"

    7. In the Expression text area, enter the following EL expression and click OK.

      ${'Hello World'}
      

      Your scenario diagram should look like the one shown in Figure 67-25.

      Figure 67-25 Scenario Editor - hello_world Scenario

      Description of Figure 67-25 follows
      Description of "Figure 67-25 Scenario Editor - hello_world Scenario"

    8. From the File menu, select Save to save the hello_world.scenarios_diagram.

  7. Create a JSP page that invokes the hello_world scenario and displays the results.

    Before creating the JSP page we need to add some libraries to the Portal project that contain resources required by the JSP page.

    1. Right-click Portal project in the Application Naviagtor and select Project Properties...

      The Project Properties dialog displays.

    2. Select the Libraries and Classpath category, and click Add Library...

      The Add Library dialog displays (see Figure 67-26).

      Figure 67-26 Project Properties Dialog - Adding the Extension Libraries

      Description of Figure 67-26 follows
      Description of "Figure 67-26 Project Properties Dialog - Adding the Extension Libraries"

    3. Select the following Extension library from the list and click OK to add it.

      Webcenter Personalization Client
      
    4. Click OK on the Project Properties dialog when finished.

      Now we can create the JSP page that executes the hello_world scenario on the Personalization Conductor and displays the result.

    5. Right-click the Portal project in the Application Naviagtor and select New...

      The New Gallery appears.

    6. From the Current Project Technologies tab, select the JSP category.

    7. From the item list select JSP and click OK.

      The Create JSP dialog displays (see Figure 67-27).

      Figure 67-27 Create JSP Dialog

      Description of Figure 67-27 follows
      Description of "Figure 67-27 Create JSP Dialog"

    8. Enter the File Name as hello_world.jsp and click OK to create the JSP page.

    9. When the JSP Editor launches, select the Source view and replace the template text with the following JSP code:

      Notice in the c:out command how the Personalization Context Bean p13nContext is being used to invoke the Conductor connection (Conductor) you created earlier using the scenario namespace Hello_Application specified earlier in the Application Properties dialog, invoking the scenario called hello_world, and finally displaying the results.

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
      "http://www.w3.org/TR/html4/loose.dtd">
      <%@ page contentType="text/html;charset=UTF-8"%>
      <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
      <html>
        <head>
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
          <title>hello_world</title>
        </head>
        <body>
          <jsp:useBean id="p13nContext"
      class="oracle.wcps.client.PersonalizationContext" scope="session"/>
          <%((oracle.wcps.client.PersonalizationContext)session.getAttribute("p13nContext")).reset(); %>
          <c:out value="${p13nContext.conductor['Conductor'].namespaces['Hello_
      application'].scenario['hello_world'].results}"/>
        </body>
      </html>
      

      Note:

      If you haven't configured single sign-on and the trust service as outlined in step 3, then the Conductor URL connection specified here will generate a 401 "Unauthorized" return code when the page is run. As a shortcut (for testing purposes only) you can substitute an inline connection:

      http://weblogic:weblogic1@localhost:7101/wcps/api/conductor/resourceIndex

      in place of the Conductor URL to get around the single sign-on constraint.

    10. From the File menu, select Save to save the hello_world.jsp.

  8. Finally, to build and deploy the Hello_Application, right-click the runnable artifact the hello_world.jsp page in the Application Navigator and select Run.

    JDeveloper starts several background processes in sequence, including:

    • Building the application,

    • Starting the intergated server (if not already started),

    • Deploying the application artifacts (including any Personalization artifacts) to the default domain, and

    • Launching an external web browser to display the artifact hello_world.jsp.

    Figure 67-28 Application Navigator - Building and Deploying the Application

    Description of Figure 67-28 follows
    Description of "Figure 67-28 Application Navigator - Building and Deploying the Application"

    The resulting hello_world.jsp JSP page appears in the browser.

    Figure 67-29 Hello_Application's hello_world.jsp Page Displayed in Browser

    Description of Figure 67-29 follows
    Description of "Figure 67-29 Hello_Application's hello_world.jsp Page Displayed in Browser"

67.4 Extending Personalization

This section describes how you can create new ways to personalize application flow or content by adding custom providers and locators.

This section includes the following sub-sections:

67.4.1 Adding Custom Providers

Providers provide an extension point to Personalization, with which you can add custom function providers and custom data providers. Table 67-2 shows the available extension points for Function and Data Providers.

Table 67-2 Extension Points

Extension Description

oracle.wcps.provider.IFunctionProvider

A Function Provider extension point for the Conductor to create utility methods for data manipulation/transformation, business rule calculation, and so forth that can be invoked using EL. A simple example might be: ${strings:concat('string1','string2')}

oracle.wcps.provider.IDataProvider

A Data Provider extension point for the Conductor. Custom data providers can be written to integrate with legacy systems or external data stores.


67.4.1.1 Extending the AnnotatedConfiguration Class

The AnnotatedConfiguration class describes the connection parameters required for a data provider, and can be found in:

Location: \Oracle\Middleware\jdeveloper\webcenter\modules\wcps-services_11.1.1.4.0\connections-service-1.0.jar

You will need to extend this class and add any other properties your custom provider may require. Create this class first, as the other components will build upon it (see Example 67-3).

Example 67-3 HelloWorldConnectionConfig

import oracle.wcps.connection.annotation.ConnectionProperty;
import oracle.wcps.connection.configuration.AnnotatedConfiguration;
 
@ConnectionConfiguration
(connectionType="hello.world.provider.connection")
@L10n
(bundle="demo.provider.resources.HelloWorldProviderResources")
public class HelloWorldConnectionConfig extends AnnotatedConfiguration
{
  @ConnectionProperty(propertyName="sampleProperty", required=true)
  String sampleProperty;
}

67.4.1.2 Implementing the Required Interfaces

IMetadata

Location: \Oracle\Middleware\jdeveloper\webcenter\modules\wcps-services_11.1.1.4.0\conductor-server-1.0.0.0.jar

public interface IMetadata
{
    public String getName();
    public Set<? extends IMetadata> getMetadata();
}

Next, implement the IMetadata interface. This provides information to the tools to surface the connection parameters, essentially representing the information in your HelloWorldConnectionConfig class as metadata (see Example 67-4). Each of the properties in the HelloWorldConnectionConfig class becomes a child metadata object. This is not so intuitive for simple structures, but is useful when the connection configuration may contain nested classes that relate to other components in the 'model'.

Example 67-4 HelloWorldConfigMetadata

public class HelloWorldConfigMetadata implements IMetadata
{
    private String name;
    private Object value ;
    private Map<String, IMetadata> childMDataMap = new HashMap<String, IMetadata>();
 
    public HelloWorldConfigMetadata(HelloWorldConnectionConfig config)
    {
        name = config.name;
    }
 
    public HelloWorldConfigMetadata(String name, Object value)
    {
        this.name = name;
        this.value = value;
    }
 
    public void addMetadata(IMetadata childMetadata)
    {
        childMDataMap.put(childMetadata.getName(), childMetadata);
    }
 
    public String getName()
    {
        return name;
    }
 
    public Object getValue()
    {
        return value;
    }
 
    public Object getProperty(String propName)
    {
        IMetadata md = childMDataMap.get(propName);
        if (md != null)
        {
            return ((HelloWorldConfigMetadata)md).getValue();
        }
        return null;
    }
 
    public Set<IMetadata> getMetadata()
    {
        Set<IMetadata> rv = new HashSet<IMetadata>();
        Set<String> childKeySet = childMDataMap.keySet();
        for (String key : childKeySet)
        {
            rv.add(childMDataMap.get(key));
        }
 
        return rv;
    }
 
    public Properties getProperties()
    {
        Properties props = new Properties();
        Set<String> childKeySet = childMDataMap.keySet();
        for (String key : childKeySet)
        {
            HelloWorldConfigMetadata cmd =
                (HelloWorldConfigMetadata)childMDataMap.get(key);
            props.put(key, cmd.getValue());
        }
        return props;
    }
 
    @Override
    public String toString()
    {
        return (name + ", properties: " + getProperties());
    }
}

IExecutableResource

An executable resource is a class having public methods annotated to designate them as executable by the Conductor. These methods could execute code directly, or delegate the call to an external REST service.

Location:

\Oracle\Middleware\jdeveloper\webcenter\modules\wcps-services_11.1.1.4.0\conductor-server-1.0.0.0.jar

public interface IExecutableResource
{
    public Object execute();
}

Your ExecutableResources can specify additional methods for execution by virtue of this annotation. Note the use of resource bundles (see section on Resource Bundles for more info). You can create any number of ExecutableResources and add them to your connection (see Example 67-5).

Example 67-5 SayHelloExecutableResource

package demo.provider;
 
import oracle.wcps.annotation.PublicFunction;
import oracle.wcps.annotation.PublicParameter;
import oracle.wcps.provider.IExecutableResource;
 
public class SayHelloExecutableResource implements IExecutableResource
{
    // This method will execute if no specific method is given
 
    public Object execute()
    {
        return "Hello from my custom provider";
    }
 
   // This method can be named explicitly in a Scenario to be invoked
 
    @PublicFunction(functionName = "sayHello",
                    descriptionBundleKey = "provider.method.sayHello.description")
    public String sayHello(@PublicParameter(parameterName = "user",
                                            descriptionBundleKey =
                                            "provider.parameter.sayHello.user.description")
        String user)
    {
        return "Hello, " + user + ".  Enjoy using the new Personalization feature!";
    }
}

IConnection

Once you have implemented your IExecutableResources and IMetadata, you can construct your actual connection object. Each connection has a specific set of configuration parameters, so your provider could possibly have multiple connection objects (see Example 67-6).

Location:

\Oracle\Middleware\jdeveloper\webcenter\modules\wcps-services_11.1.1.4.0\conductor-server-1.0.0.0.jar

public interface IConnection
{
    public void setScenarioContext(IScenarioContext context);
 
    public Map<String, IExecutableResource> getNamedResources();
 
    public IExecutableResource getDefaultResource();
 
    public Map<String, IMetadata> getConnectionMetadata();
 
}

Example 67-6 HelloWorldConnection

package demo.provider;
 
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
 
import java.util.concurrent.ConcurrentHashMap;
 
import oracle.wcps.provider.IConnection;
import oracle.wcps.provider.IExecutableResource;
import oracle.wcps.provider.IMetadata;
import oracle.wcps.scenario.IScenarioContext;
 
public class HelloWorldConnection implements IConnection
{
    private IScenarioContext scenarioContext;
 
    private Map<String, IExecutableResource> executableResources =
        new ConcurrentHashMap<String, IExecutableResource>();
 
    private IExecutableResource defaultResource;
    private HelloWorldConfigMetadata configMetadata;
    private Map<String, IMetadata> metadataMap = new HashMap<String, IMetadata>();
 
    /**
     * Create metadata about this connection from the incoming config
     * @param config
     */
    public HelloWorldConnection(HelloWorldConnectionConfig config)
    {
        configMetadata = new HelloWorldConfigMetadata(config);
 
        // Loop over config properties and add as child metadata
        Iterator<Map.Entry<String, Object>> iter =
            config.entrySet().iterator();
        while (iter.hasNext())
        {
            Map.Entry<String, Object> entry = iter.next();
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value != null)
            {
                configMetadata.addMetadata(new HelloWorldConfigMetadata(key,
                                                                        value));
            }
        }
        metadataMap.put(config.name, configMetadata);
 
        // Now create the executable resources for this connection
        createExecutableResources(config);
    }
 
    public void setScenarioContext(IScenarioContext scenarioContext)
    {
        this.scenarioContext = scenarioContext;
    }
 
    public Map<String, IExecutableResource> getNamedResources()
    {
        return executableResources;
    }
 
    public IExecutableResource getDefaultResource()
    {
        return defaultResource;
    }
 
    public Map<String, IMetadata> getConnectionMetadata()
    {
        return metadataMap;
    }
 
    /**
     * Create the IExecutableResources for this connection. An executable resource
     * is a class having one or more public methods.
     * @param config
     */
    private void createExecutableResources(HelloWorldConnectionConfig config)
    {
        // NOTE: you may or may not need to use the config information with your executable resources
 
        IExecutableResource helloResource = new SayHelloExecutableResource();
        IExecutableResource addingResource = new AddingExecutableResource();
 
        executableResources.put("HelloResource", helloResource);
        executableResources.put("AddingResource", addingResource);
 
        // Make one of them the default
        defaultResource = helloResource;
 
    }
}

An instance of your HelloWorldConnection will be instantiated for each entry in your provider-connections.xml file that corresponds to the connection type specified in HelloWorldConnectionConfig (connectionType="hello.world.provider.connection").

IDataProvider

Finally, we are ready to roll all this into our top-level provider class HelloWorldProvider. This is the access point for the Conductor to configure our connections and invoke our IExecutableResource(s).

Location:

\Oracle\Middleware\jdeveloper\webcenter\modules\wcps-services_11.1.1.4.0\conductor-server-1.0.0.0.jar

Project Properties -> Libraries and Classpath -> add Jar/Directory -> (point to above conductor-server-1.0.0.0.jar)

Example 67-7 IDataProvider

public interface IDataProvider
{
    /**
     * Returns a map of connections keyed by connection name
     */
    public Map<String, ? extends IConnection> getNamedConnections();
 
    /**
     * Returns the "default" connection to use
     */
    public IConnection getDefaultConnection();
 
    /**
     * Called by the framework to set the current scenario context.
     */
    public void setScenarioContext(IScenarioContext scenarioContext);
 
    /**
     * Return the annotated class used to house connection configuration.
     *
     * <p>
     * Returning null disables this feature.
     */
    public Class<?> getConnectionConfigClass();
 
 
    /**
     * If getConnectionConfigClass returns a valid configuration class,
     * configurations are provided through this method, if any exist.
     * @param configurations The collection of configurations.
     */
    public void setConnectionConfigurations(List<?> configurations);
}

Required annotations:

@ContextualProvider
        (
                contextName="HelloWorldProvider",
                resourceBundle="demo.provider.resources.HelloWorldProviderResources",
                nameBundleKey="provider.name",
                descriptionBundleKey="provider.description"
        )

See Example 67-8 on resource bundles below for more information.

Example 67-8 Resource Bundles

package demo.provider;
 
import java.util.Collections;
import java.util.List;
import java.util.Map;
 
import java.util.concurrent.ConcurrentHashMap;
 
import oracle.wcps.annotation.ContextualProvider;
import oracle.wcps.provider.IConnection;
import oracle.wcps.provider.IDataProvider;
import oracle.wcps.scenario.IScenarioContext;
@ContextualProvider
        (
                contextName="HelloWorldProvider",
                resourceBundle="demo.provider.resources.HelloWorldProviderResources",
                nameBundleKey="provider.name",
                descriptionBundleKey="provider.description"
        )
public class HelloWorldProvider implements IDataProvider
{
  // Never used, but there is a required public method to set this.
  private IScenarioContext scenarioContext;
 
  // ConnectionConfigurations may come in via classes/META-INF/connection-providers.xml
   private List<HelloWorldConnectionConfig> connectionConfigurations;
 
  // Each IConnection is uniquely identified via the AGConnectionConfig
  private static Map<String, HelloWorldConnection> connections =
              new ConcurrentHashMap<String, HelloWorldConnection>();
 
  // One of the connections will become the default
  private IConnection defaultConnection;
 
    public HelloWorldProvider() {
        super();
    }
 
    /**
     * Return a map of all our Connections, keyed by config.name.
     * @return
     */
    public Map<String, HelloWorldConnection> getNamedConnections()
    {
        return connections;
    }
 
    /**
     * One of the connections will be designated the default
     * @return
     */
    public IConnection getDefaultConnection()
    {
        return defaultConnection;
    }
 
    /**
     * Used to hold parameters specific to a given scenario
     * @param scenarioContext
     */
    public void setScenarioContext(IScenarioContext scenarioContext)
    {
        this.scenarioContext = scenarioContext;
    }
 
    /**
     * The config class is specific to each data provider
     * @return
     */
    public Class<HelloWorldConnectionConfig> getConnectionConfigClass()
    {
        return HelloWorldConnectionConfig.class;
    }
 
    /**
     * Create a HelloWorldConnection instance from each of the incoming configs.
     * These configs are created when the provider-connections.xml file is loaded.
     * @param configs
     */
    public void setConnectionConfigurations(List<?> configurations)
    {
        List<HelloWorldConnectionConfig> configs = (List<HelloWorldConnectionConfig>)configurations;
        for (HelloWorldConnectionConfig config : configs)
        {
            // Create a HelloWorldConnection from the config
            HelloWorldConnection connection = new HelloWorldConnection(config);
            if (config.isDefault)
            {
                defaultConnection = connection;
            }
            connections.put(config.name, connection);
        }
    }
}

67.4.1.3 Creating the Resource Files

These files also go in the same directory as your classes, so they will be added to your final JAR file. The location of the files must correspond to the package name given in HelloWorldProvider:bundle="demo.provider.resources.HelloWorldProviderResources"

File name: HelloWorldProviderResources.properties

File contents: provider.name=Hello World
provider.description=Data provider for Hello World

provider.parameter.sayHello.user.description=Method to say hello to a named user
provider.parameter.addTwoIntegers.int1.description=First number to add
provider.parameter.addTwoIntegers.int2.description=Second number to add

67.4.1.4 Wiring the Data Provider to the Conductor

Configure your connection in as described in

Name your provider for the Conductor by creating this file in the META-INF/services directory (in the same directory structure as your classes, so they will be added to your final jar file):

file name: oracle.wcps.provider.IDataProvider
file contents: demo.provider.HelloWorldProvider (fully-qualified name of your provider) 

67.4.1.5 Invoking your Data Provider in a Scenario

Note the resource name is the same as in your HelloWorldConnections source code.

Example 67-9 Scenario Calling the Provider

<scenario:scenario xmlns:common="http://xmlns.oracle.com/wcps/conductor/common/1.0.0"
                   xmlns:scenario="http://xmlns.oracle.com/wcps/conductor/scenarios/1.0.0">
    <body>
        <call-provider>
            <provider-name>HelloWorldProvider</provider-name>
            <connection-name>HelloWorldConnection</connection-name>
            <resource-name>HelloResource</resource-name>
            <method-name>sayHello</method-name>
            <variable>providerResults</variable>
            <input>
                <parameter>
                    <common:name>user</common:name>
                    <common:value>cindymc</common:value>
                </parameter>
            </input>
        </call-provider>
        <return>
            <expression>${providerResults}</expression>
        </return>
    </body>
    <name>TestMyProviderScenario</name>
</scenario:scenario>

67.4.2 Adding Custom Locators

This section describes how you can extend your personalized WebCenter Portal application by adding custom locators.

This section contains the following subsections:

67.4.2.1 Understanding Property Locators

A property locator is invoked by the Property Service to access external stores of properties. For example, Personalization provides an out-of-the-box PeoplePropertyLocator that uses the People Connections REST service to access profile information for users. The locator is not invoked directly by the Conductor/Scenario, but is instantiated by the Property Set service, which then delegates calls for retrieving properties and property sets to the underlying locator implementation.

The example in the following sections will walk you through creating your own BookPropertyLocator to find a book by title in a library, and load its attributes into a property set you have created to represent a Book object.

There are two major steps to implementing this example:

  1. Define the PropertySet for Book, and let the Property Set service know about it

  2. Implement the IPropertyLocator to load properties from a Book object.

67.4.2.2 Implementing the Required Interfaces

Location:

\Oracle\Middleware\jdeveloper\webcenter\modules\wcps-services_11.1.1.4.0\properties-common-1.0.0.0.jar

public interface IPropertyLocator
{
    public void loadProperties(IContext context, IPropertySet propertySet,
                               IPropertySetDefinition propertySetDefinition,
                               List<IPropertyName> propertyNames);
 
    public void storeProperties(IContext context,  propertySet,
                                IPropertySetDefinition propertySetDefinition,
                                List<IPropertyName> propertyNames);
 
    public void removeProperties(IContext context,  propertySet, List<IPropertyName> propertyNames);
 
    public void removeProperties(IContext context,  nameSpaceName, IPropertySetDefinitionName name);
 
    public IPagedList<IPropertySet> filter(IContext context,  nameSpaceName,
                                           IPropertySetDefinitionName setDefinitionName, PropertySetFilterContext filterContext);
 
    public int count(IContext context,  nameSpaceName, PropertySetExpression expression);
}

67.4.2.3 Implementing Security

The IContext object is the vehicle for authentication, passed in from the Property Service. There are two ways you can access the credentials:

  • Using the request: HttpServletRequest request = context.getProperty(IContext.HTTP_REQUEST_KEY)

  • Using the user/password: String user = context.getProperty(IContext.USERNAME_KEY); String pw = context.getProperty(IContext.PASSWORD_KEY);

67.4.2.4 Creating a Property Set Definition

This section describes how to create a PropertySetDefinition that will be used with your locator.

Follow the steps below to create the PropertySetDefinition:

  1. Create a new JDeveloper project for your source files.

  2. Add the required Personalization classes to the project classpath.

    1. Right-click on the project name and select Project Properties.

    2. Select Libraries and Classpath, then select Add Library.

    3. Enter the name of the library to search for (for example, IPropertyLocator) and select it when it appears.

    4. Repeat these steps for other classes your source code will need to reference.

  3. Compile the project and deploy it to a JAR file.

  4. Copy the JAR file to the Personalization extensions directory in /DefaultDomain/locator-extensions-library/WEB-INF/lib.

67.4.2.5 Putting It All Together

This section describes how to implement the property locator, data object, and data layer.

Implement the IPropertyLocator

(read-only for demo simplicity)

/**
 * Our locator will be read-only
 */
public class BookPropertyLocator implements IPropertyLocator
{
    // Let's delegate all the hard work to a BookPropertyDAO
    private BookPropertyDAO dao;
 
    // Must have a public default constructor, since the Property Service instantiates
    // this
 
    public BookPropertyLocator()
    {
        // todo: show how connection properties could be initialized and passed in
        dao = new BookPropertyDAO();
    }
 
    /**
     * Load property values, optionally returning only a subset of those.
     * @param iPropertySet The PropertySet into which the retrieved values will be loaded
     * @param iPropertySetDefinition The PropertySetDefinition defining the 'shape' of the properties
     * @param list List of PropertyNames to load.  If null, all will be loaded.
     */
    public void loadProperties(IContext context, IPropertySet iPropertySet,
                               IPropertySetDefinition iPropertySetDefinition,
                               List<IPropertyName> list)
    {
        try
        {
            // Delegate to BookProperDAO
            dao.loadProperties(iPropertySet, list);
        } catch (Exception e)
        {
            throw new RuntimeException("Cannot load properties ", e);
        }
    }
 
 
    /**
     * Filter properties; load/return those properties that only match the filter criteria.
     * @param iNameSpaceName
     * @param iPropertySetDefinitionName
     * @param propertySetFilterContext
     * @return
     */
    public IPagedList<IPropertySet> filter(IContext context, INameSpaceName iNameSpaceName,
                                           IPropertySetDefinitionName iPropertySetDefinitionName,
                                           PropertySetFilterContext propertySetFilterContext)
    {
        return null;   // todo: possibly implement for demo
    }
 
    public int count(IContext context, INameSpaceName iNameSpaceName,
                     PropertySetExpression propertySetExpression)
    {
        return 0;
    }
 
 
    // As a read-only implementation, we don't support this
    public void storeProperties(IContext context, IPropertySet iPropertySet,
                                IPropertySetDefinition iPropertySetDefinition,
                                List<IPropertyName> list)
    {
        throw new UnsupportedOperationException("BookPropertyLocator is read-only");
    }
 
    // As a read-only implementation, we don't support this
    public void removeProperties(IContext context, IPropertySet iPropertySet,
                                 List<IPropertyName> list)
    {
        throw new UnsupportedOperationException("BookPropertyLocator is read-only");
    }
 
    // As a read-only implementation, we don't support this
    public void removeProperties(IContext context, INameSpaceName iNameSpaceName,
                                 IPropertySetDefinitionName iPropertySetDefinitionName)
    {
        throw new UnsupportedOperationException("BookPropertyLocator is read-only");
    }
}

Implement the Data Access Object for retrieving property values

/**
 * Data access class, to load books from a backing store (perhaps a database or
 * some REST service)
 */
class BookPropertyDAO
{
    // Generate the library that will provide our backing store.
    static
    {
        Library.generateLibrary();
    }
 
    /**
     * Assume the name of the PropertySet is the title of the book.
     * @param propertySet
     * @param list
     */
    void loadProperties(IContext context, IPropertySet propertySet,
                        List<IPropertyName> list) throws Exception
    {
        String title = propertySet.getPropertySetName().getName();
        Book book = Library.getBook(title);
        loadProperties(book, propertySet, list);
    }
 
    private void loadProperties(IContext context, Book book, IPropertySet propertySet,
                                List<IPropertyName> propertyNames) throws Exception
    {
        // Loop over all property names, create a Property value, and add it to the Property set
        for (IPropertyName pname : propertyNames)
        {
            // System.err.println("Getting property: " + pname + ", with name: " + pname.getName());
            String value = book.getProperty(pname.getName());
            if (value != null)
            {
                addProperty(value, pname, propertySet);
            }
        }
    }
 
    private void addProperty(IContext context, String value, IPropertyName pname,
                             IPropertySet propertySet)
    {
 
        IProperty<String> ps =
            Property.<String>builder().withProperty(pname, value).buildWithOutValidation();
        propertySet.putProperty(pname, ps);
    }
}

Implement the data layer (Books within a Library)

/**
 * Simple class to demonstrate loading properties.  It could be improved by adding
 * support for non-String attributes (Date, arrays, etc) and using Java Bean
 * Introspection/Reflection to get the property values.
 */
public class Book
{
    String title;
    String author;
    String ISBN;
 
    public Book(String title, String author, String ISBN)
    {
      setTitle(title);
      setAuthor(author);
      setISBN(ISBN);
    }
 
    public void setTitle(String title)
    {
        this.title = title;
    }
 
    public String getTitle()
    {
        return title;
    }
 
    public void setAuthor(String author)
    {
        this.author = author;
    }
 
    public String getAuthor()
    {
        return author;
    }
 
    public void setISBN(String ISBN)
    {
        this.ISBN = ISBN;
    }
 
    public String getISBN()
    {
        return ISBN;
    }
 
    String getProperty(String propName)
    {
         // Here, we could use introspection on the Book class, but since there are only
         // three properties, we can code it simpler
         if (propName.equals("ISBN"))
         {
           return getISBN();
         }
         if (propName.equals("title"))
         {
           return getTitle();
         }
         if (propName.equals("author"))
         {
           return getAuthor();
         }
         return null;  // todo: perhaps throw Exception here
    }
}
/**
 * Class to generate a library of Books
 */
class Library
{
    static String [] titles = {"Into Thin Air", "Climbing Everest", "Rocky Mountains"};
    static String [] ISBNs = {"0-201-69581-2", "0-423-78123-1", "0-321-23425-2"};
    static String [] authors = {"Jon Krakauer", "Fooey Handley", "Rick Bicknell"};
 
    // Keyed by title
    private static Map<String, Book> library = new HashMap<String,Book>();
 
    static void generateLibrary()
    {
        for (int i=0; i<titles.length; i++)
        {
            Book book = new Book(titles[i], authors[i], ISBNs[i]);
            library.put(book.getTitle(), book);
        }
    }
 
   static Book getBook(String title)
   {
     return library.get(title);
   }
}

67.4.2.6 Invoking the Property Locator

The PropertyLocator is invoked by the Property Service, which is itself part of the PropertiesServiceProvider. We can invoke the GetProperties method on that provider in a scenario, as shown below:

<scenario:scenario xmlns:common="http://xmlns.oracle.com/wcps/conductor/common/1.0.0" xmlns:scenario="http://xmlns.oracle.com/wcps/conductor/scenarios/1.0.0">
  <body>
  <call-provider>
      <provider-name>oracle.PropertiesServiceProvider</provider-name>
      <method-name>GetProperty</method-name>
      <variable>property</variable>
      <input>
        <parameter>
            <common:name>namespace</common:name>
            <common:value>book.property.locator</common:value>
        </parameter>
        <parameter>
            <common:name>definition</common:name>
            <common:value>BookPropertySetDefinition</common:value>
        </parameter>
        <parameter>
            <common:name>set</common:name>
            <common:value>Into Thin Air</common:value>
        </parameter>
        <parameter>
            <common:name>property</common:name>
            <common:value>ISBN</common:value>
        </parameter>
      </input>
    </call-provider>
    <return>
      <expression>${property}</expression>
    </return>
  </body>
  <name>BookPropertyScenario</name>
</scenario:scenario>