Skip Headers
Oracle® Fusion Applications Developer's Guide
11g Release 1 (11.1.1.5)

Part Number E15524-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

31 Advanced Topics for ECSF

This chapter provides information on advanced topics for Oracle Enterprise Crawl and Search Framework (ECSF).

This chapter includes the following sections:

31.1 Introduction to Advanced Topics for ECSF

ECSF offers additional functionality to enhance the search experience. In addition to search on business objects, ECSF supports search on Fusion file attachments, WebCenter tags, and tree structure-based source systems. ECSF also allows you to set up federated search so that users can search across Oracle Fusion Applications product families or across multiple Oracle Secure Enterprise Search (Oracle SES) instances.

Advanced topics also include using the external ECSF web service to integrate ECSF with Oracle Fusion Applications, localizing ECSF artifacts for international users, and information for troubleshooting ECSF.

31.2 Enabling Search on Fusion File Attachments

ECSF supports the capability to crawl Oracle Fusion Applications file attachments that are associated with ECSF searchable objects and stored in the Oracle Enterprise Content Management Suite repository.

31.2.1 How to Make File Attachments Crawlable

References to files in the content repository are stored in a special database table and are retrieved by using a view object named oracle.apps.fnd.applcore.attachments.uiModel.view.AttachmentsVO. Using a view link, you can make the AttachmentsVO a child of another view object. If a searchable object has a child AttachmentsVO, then ECSF automatically makes sure that the attachments are crawled when the searchable object is crawled.

At the time the searchable object is crawled, ECSF includes a content link in the document that is sent to Oracle SES for each attachment. When Oracle SES receives the data feed and finds the content link, it calls back to ECSF to retrieve the content of the attachment. ECSF then invokes an application programming interface (API) method that retrieves the attachment content from the content server and returns it to Oracle SES. Oracle SES indexes the searchable object and attachment content as one item.

The API handles authentication and authorization.

To make file attachments crawlable:

  1. Add the Applications Core (Attachments) library to your project.

    This adds the Attachment-Model.jar and Common-Model.jar files, located in JDEV_HOME\jdeveloper\jdev\oaext\adflib, to your projects.

  2. Create a view link between the searchable object and the AttachmentsVO to make AttachmentsVO a child of the searchable object.

    Note:

    When you design your view object for search, make sure that you configure view links to generate only the Destination Accessor and not the Source Accessor.
  3. On the searchable object, define a view link accessor that points to the view link. For information, see Section 18.2.2, "How to Create Attachment View Links".

31.3 Enabling Search on WebCenter Tags

ECSF supports the capability to crawl searchable objects with Oracle WebCenter tags so that tags can be used as keywords or filters for search in Oracle Fusion Applications. A tag is a meaningful term attached to an object. Tags can be used for various purposes such as categorization, to-dos, and priorities. For more information, see the "Integrating the Tags Service" chapter in Oracle Fusion Middleware Developer's Guide for Oracle WebCenter.

Note:

ECSF does not support search using private tags.

Tags are single words stored as space-separated strings in the application database and are retrieved by using a view object called TagSVO (service view object). You must create a view link to make TagSVO a child object. At crawl time, the view link is used to locate TagSVO. Figure 31-1 illustrates crawl time with tags.

Figure 31-1 Crawl Time With Tags

Crawl time with tags

Purchase Order 123 has two tags, Computer and Dell. ECSF adds the tags for each record of the searchable object to the indexable document before the document is sent to Oracle SES for indexing. ECSF also creates a reserved attribute (of type string) called ECSF_TAGS to store tags in Oracle SES.

At query time, users can specify tag values as keywords or as filters. When tag values are input as keywords, the tag value is treated as a query string and returns results that include the objects with the specified tag. When tag values are used as filters, the tags are added to QueryMetaData and the query is run with filters on ECSF_TAGS. Only the objects with the specified tags are returned. Figure 31-2 and Figure 31-3 illustrates the difference between query time without a tag and query time with a tag.

Note:

Tags cannot be searched as individual entities.

Figure 31-2 Query Time Without Tag

Query time without tag

In Figure 31-2, the indexed document for Purchase Order 123 contains two tags, Computer and Dell. The query on John returns all the documents that contain John. The results display the title, body, and all tags for the documents.

Figure 31-3 Query Time With Tag

Query time with tag

In Figure 31-3, query on the Purchase Order John and tag Dell returns only the documents that contain John AND the Dell tag. The results display the title, body, and all tags for the documents. If both Dell and Computer were specified as tags, then the query would return only the documents that contain both tags (that is, Dell AND Computer). You cannot specify tags using the OR condition (that is, Dell OR Computer), so the query cannot return documents that contain either the Dell tag or the Computer tag.

Enabling search on WebCenter tags allows tags to be added to indexable documents and stored in the reserved attribute called ECSF_TAGS in Oracle SES. Tags can then be used as keywords or filters for search.

Perform the following tasks to enable search on WebCenter tags:

  1. Create a view link between the searchable object and the TagSVO. For information, see Section 14.15.5, "How to Implement Tags in Oracle Fusion Applications Search".

    Note:

    When you design your view object for search, make sure that you configure view links to generate only the Destination Accessor and not the Source Accessor.

    The view link must use the accessor name tagSVO so that the search extension can navigate to the child object when it crawls.

  2. Add tags to the indexable document.

  3. Add tags to the query.

You can also perform the following tasks to customize search on WebCenter tags:

  1. Modify the tags in the indexable document.

  2. Register change listeners.

31.3.1 How to Add Tags to Indexable Documents

In order to crawl tags, you must use Tag APIs to add tags to indexable documents. You can use Tag APIs in the search extension code.

To add tags to indexable documents:

  1. Create a search extension that extends DefaultSearchPlugin, implements PreIndexProcessor, and includes a method called preIndexProcess that adds tags to some objects.

    Note:

    If you place the search extension in the Oracle WebLogic Server shared library, then the ECSF library (ecsf.jar) must be present in the shared library in order for ECSF to load the PreIndexProcessor interface.
  2. Redeploy and crawl the objects.

You can implement code, such as the sample code shown in Example 31-1, in the search extension to extend DefaultSearchPlugin.

Example 31-1 Sample Code for Adding Tags to Indexable Documents

public class runtime.TestPlugin extends DefaultSearchPlugin implements PreIndexProcessor                                                          
{
   public void preIndexProcess(SearchContext ctx, List <IndexableDocument> documents)
   {
      for( IndexableDocument doc : documents)
      {
             Object ename = doc.getFieldValue("ENAME");
     if (ename != null && "Zebra".equalsIgnoreCase(ename.toString()))
      {
             doc.addTags(new String[] { "Black","White","Stripes" });
   //doc.getTags and doc.clearTags can also be used here
    for(String tag : doc.getTags())
    {
system.err.println(tag);//print out tag to stand err
                 }
}
      }
   }
}

This extension adds three tags (Black, White, and Stripes) to a user named Zebra.

31.3.2 How to Add Tags for Querying

After tags are crawled into Oracle SES, you can perform keyword searches on tags or filter on tags. QueryMetaData accepts tags for querying. When one or more tags are added to QueryMetaData, the query runs with filters on ECSF_TAGS.

Example 31-2 illustrates how tags are used for querying.

Example 31-2 Sample API for Querying With Tags

public void tagTest()
   {
      SearchCtrl searchCtrl = new SearchCtrl();
      SearchHits searchHits = null;
      SearchContext searchContext = null;
      QueryMetaDataImpl queryMetaData = new QueryMetaDataImpl();
      queryMetaData.setQueryString("%");
      queryMetaData.setPageSize(10);
      queryMetaData.setCurrentPage(1);

      String engineInstName = "SES";
      String groupName = "runtime.EmpView";
      SearchGroup[] sgs = new SearchGroup[] {searchCtrl.getSearchGroup(engineInstName , groupName);

      queryMetaData.setSearchGroups(sgs);
      searchContext = ContextFactory.getSearchContext();
      searchContext.bindUser("scott");
      try
      {
//clear tags so query tag through keywords
         queryMetaData.clearTags();
         queryMetaData.setQueryString("Black White");
         searchHits = searchCtrl.runQuery(searchContext, queryMetaData);
//Zebra is found

//filter by tags
         queryMetaData.setQueryString("%");
         queryMetaData.addTag("White");
         searchHits = searchCtrl.runQuery(searchContext, queryMetaData);
//Mickey is found

//filter by tags
         queryMetaData.addTag("White");
         queryMetaData.addTag("Black");
         searchHits = searchCtrl.runQuery(searchContext, queryMetaData);
//Zebra is found
//filter by tag that exists and tag that does not exist
         queryMetaData.clearTags();
         queryMetaData.addTag("Stripes");
         queryMetaData.addTag("Dots");
         searchHits = searchCtrl.runQuery(searchContext, queryMetaData);
//no result is found

//filter by tag that does not exist
        queryMetaData.clearTags();
        queryMetaData.addTag("Dots");
         searchHits = searchCtrl.runQuery(searchContext, queryMetaData);
//no result is found
   }

Adding tags for querying forces the query to find results where indexed documents contain the added tags. When more than one tag is added, the resulting documents must contain both tags. Documents containing only one of the tags are not returned.

Query SES retrieves tags all the time. The searcher adds tags to IndexedDocument by using setTags. In case of null in the attribute, no tags are added. IndexedDocument.getTags returns all the tags in the document.

31.3.3 How to Modify Tags in Indexable Documents

You can override tags in the lifecycle methods provided by ECSF. For example, if required, you can add tags to an indexable document in the IndexablePostProcess by using the APIs provided on the IndexableDocument:

  • void addTags(String[] tags) adds a list of strings as tags to the document. Duplicates are removed.

  • Collection<String> getTags() returns a list of tags associated with the document.

  • void clearTags() clears tags associated with the document.

31.3.4 How to Register Change Listeners

You can extend the incremental crawling mechanism by registering change listeners, which use the WebCenter tagging framework to identify objects that need to be updated in the search engine. Implement the oracle.ecsf.ChangeListener interface in a search extension to customize the runtime logic that detects changes in the searchable objects. Example 31-3 illustrates a sample implementation of ChangeListener.

Example 31-3 Sample Implementation of ChangeListener

public class TestPlugin extends DefaultSearchPlugin implements ChangeListener
{

   public Iterator getChangeList(SearchContext ctx, String changeType)
   {
      return new MyChangeIterator(ctx, changeType);
   }
}

public class MyChangeIterator extends ChangeIterator
{
   private Date lastTimeCrawled;
   private int mCount;
   private int mIndex = 0;

   public MyChangeIterator (SearchContext ctx, String changeType)
   {
      super(ctx, changeType);
      Date lastTimeCrawled = ctx.getSearchableObject().getLastTimeCrawled();
      //if this value is null, ECSF is doing an initial crawl
      if(lastTimeCrawled==null)
       {
           setDone(true);
       }else
       {
//find how many has been changed since that date
            mCount = 100; //example
       }
   
   }
   protected List populate(SearchContext ctx)
   {
      List nextList = new ArrayList();
//the following code marks Zebra has been changed
PrimaryKey pk = new PrimaryKey();
Pk.put("ENAME", "Zebra");
NextList.add(pk);

setDone(true); //tells ECSF there are no more
      return nextList;
   }
}

The method Iterator getChangeList (SearchContext ctx, String changeType) returns an iterator (ChangeIterator) over a list of primary keys for the searchable object (ctx.getSearchableObject).

The primary keys returned from the change listener are logged in the ECSF change log table before an incremental control feed is constructed.

31.4 Enabling Search on Tree Structure-based Source Systems

In addition to supporting the crawling and searching of relational-based objects through view objects or Java Database Connectivity (JDBC), ECSF supports the capability to crawl data in hierarchical tree-based data structures, or tree structures, and to identify items for indexing to enable full-text search. A tree structure is a common data structure, such as a file system on a computer hard disk, used to organize a large number of items. Oracle Business Intelligence is an example of a source system that is organized in a tree structure. For more information about Oracle Business Intelligence Suite Enterprise Edition and its supported search functionality, see the "Managing Objects in the Oracle BI Presentation Catalog" chapter in Oracle Business Intelligence Suite Enterprise Edition User's Guide.

By default, ECSF supports database crawling using Oracle ADF technology. To support crawling data that is organized in tree structures, you can extend the abstract implementation of the CrawlableFactory oracle.ecsf.data.tree.AbstractTreeWalker. The extension converts the data stored at tree nodes into documents that Oracle SES receives through Really Simple Syndication (RSS) feeds and indexes.

Note:

Crawling relies on the integrator's implementation that is based on their underlying data structures and accessibility of the items to be indexed.

Note:

ECSF currently does not provide an interface for converting information in source systems to indexable documents. It is assumed that data structures of an indexable item are proprietary to the source system, and the interface for converting an item pertaining such a structure to indexable document are the responsibility of the integrator.

A searchable object holds metadata about the source system. It can be either an Oracle ADF view object with ECSF annotation or a class that extends oracle.ecsf.meta.SearchableObject. For tree structure-based source systems, the searchable object is not view object-based, so ECSF does not load the view object. Instead, it loads a Java class that extends and implements the searchable object. When requested with a ConfigFeed URL, ECSF identifies the searchable object that holds the search metadata required by ECSF. Non-view object-based searchable objects can be grouped into search categories. Figure 31-4 illustrates the data flow for search on tree structure-based source systems.

Figure 31-4 Data Flow for Search Using ECSF Tree Crawler

Data flow for search using ECSF Tree Crawler

The integration of ECSF with the source system allows an Oracle SES instance to crawl the source system data. The Source System DataNode, which is exposed to the ECSF tree crawler, formulates the data structure and pulls data from the source system server using web services. ECSF converts the tree nodes into documents that Oracle SES receives through RSS feeds and indexes. The source system implements a security service. When data from the source system is indexed in the Oracle SES, it is guarded against access by the security service.

A security extension is needed to implement source and document-level security. A searchable object has a method of getting a search extension instance based on the metadata. To associate a search extension with a searchable object, configure it from the Search navigation tab of the overview editor in JDeveloper. For more information, see Section 27.2.3, "How to Make View Objects Searchable".

ECSF also extends its attachment implementation to enable the implementer to open the stream for data pulling attachments that are associated with a particular node. For more information, see Section 31.2, "Enabling Search on Fusion File Attachments".

Enable search on tree structure-based source systems by:

  1. Crawling tree structures

  2. Integrating search functionality for tree structures

  3. Implementing administration using ECSF interfaces

31.4.1 How to Crawl Tree Structures

ECSF offers Java classes that provide support for traversing tree structure-based data sources and identifying items for indexing. To implement search for these data sources, complete the following tasks:

  1. Create a searchable object by extending the oracle.ecsf.data.tree.SearchableTreeObject class.

  2. Implement a crawlable tree node to extract document metadata.

  3. Extend AbstractTreeWalker to traverse the tree.

  4. Implement security.

  5. Implement the attachments interface to stream the documents to Oracle SES.

  6. Deploy and start the ECSF servlet.

  7. Configure Oracle SES to crawl ECSF.

Before you begin:

Install the ECSF seed data records in the ECSF schema of the Oracle Fusion Applications database using Seed Data Framework (SDF). For information, see Chapter 55, "Initializing Oracle Fusion Application Data Using the Seed Data Loader".

31.4.1.1 Creating a Searchable Object

Create a searchable object by extending the oracle.ecsf.data.tree.SearchableTreeObject class, as shown in Example 31-4. When you create a searchable object for a tree object, you create a Java class, the SearchableTreeDirectory class, that is part of your implementation Java archive (JAR) file.

Example 31-4 Sample Code for SearchableTreeDirectory Class

package oracle.ecsf.test.tree;
import oracle.ecsf.data.tree.SearchableTreeObject;

public class SearchableTreeDirectory extends SearchableTreeObject {
    //override the security plug to be used
    public void initializeConfig() {
        setPlugInName(SecurityPlugin.class.getName());

        //add a custom attribute
        //see processNode method where you must set attribute value
        //for this attribute for the indexable document
        DocumentDefinition docdef = this.getDocument();
        FieldDefinitionImpl field = new FieldDefinitionImpl("CUSTOM_ATTR");
        field.setBinding("CUSTOM_ATTR");
        //this flag indicates that this attribute  be stored in SES
        field.setStored(true);
        docdef.addField(field);
    }

    //override the crawlable factory to be used
    public String getCrawlableFactoryName() {
        return FileTreeCrawler.class.getName();
    }
    //override the method to get the location and the name of the last crawled timestamp file
    //this is optional, if it is not overridden, the default location is the temporary directory of the
    //system, that is, in unix/linux, it is "/tmp" and in Windows, it is "c:/temp"; the default file name is
    // ".ecsf." concatenated with the object name, in this case "oracle.ecsf.test.tree.SearchableTreeDirectory"
    public String getFileName() {
        return "./.ecsf.oracle.ecsf.test.tree.SearchableTreeDirectory";
    }

}

You must override the initializeConfig method to create search metadata dynamically for your searchable object, including adding a custom attribute, setting the security plug-in class name, and so on.

You must override getCrawlableFactoryName() to return the class name of your extension of AbstractTreeWalker.

Override getFileName if you want to persist the last crawled timestamp in a particular location.

For information, see Section 31.4.1.3, "Extending AbstractTreeWalker".

31.4.1.2 Implementing a Crawlable Tree Node

A crawlable tree node represents a node in your tree structure. When crawled, each node on the tree structure is wrapped in this object. This node is created by the extension of AbstractTreeWalker. For more details on each method, see the Javadoc for ECSF.

Implement a crawlable tree node by extending the oracle.ecsf.data.tree.CrawlableTreeNode class to create a tree node class called CrawlableTreeNodeImpl, as shown in Example 31-5, for extracting document metadata. The tree node class models the tree structure repository.

Example 31-5 Sample Code for CrawlableTreeNodeImpl Class

package oracle.ecsf.data.tree;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import java.util.Date;

import oracle.ecsf.IndexableDocument;
import oracle.ecsf.meta.PrimaryKey;
import oracle.ecsf.data.tree.CrawlableTreeNode;

public class CrawlableTreeNodeImpl extends CrawlableTreeNode {
    String[] filesToHandle=new String[]{"java", "xml"};
    private File file;
    //Creates a node with a fully qualified name.
    public CrawlableTreeNodeImpl(String name) {
        super(name);
        file = new File(name);
        this.setPath(file.getPath());
        this.setName(file.getName());
    }
    //Internal. Determine whether a file should
    //be indexed.
    private boolean handleFile(File file) {
        if(file ==null)
            return false;
        if(file.isDirectory())
            return false;
        for(String ext : filesToHandle) {
            if(file.getName().endsWith(ext))
                return true;
        }
        return false;
    }
    //Test if a file needs to be indexed.
    public boolean isIndexable() {
        return handleFile(file);
    }
    /*
     * Gets the last modified date.
     */
    public Date getLastModified() {
        if (file == null) {
            return null;
        } else {
            return new Date(file.lastModified());
        }
    }

    public void processNode(IndexableDocument doc) {
        try {
            StringBuffer indexContent = new StringBuffer();
            FileReader fileReader = new FileReader(file);
            BufferedReader br = new BufferedReader(fileReader);
            String strd = br.readLine();
            while (strd != null) {
                indexContent.append("\n" + strd);
                strd = br.readLine();
            }
            if (file.getName().endsWith("xml")) {
                PrimaryKey keys = new PrimaryKey();
                keys.put("Name", file.getName());
                keys.put("Path", file.getPath());
                doc.addAttachment(new AttachmentImpl(keys));
            }
            br.close();
            fileReader.close();
            doc.setContent(indexContent.toString());
            doc.setFieldValue("CUSTOM_ATTR", "This is custom attributes to be saved in SES");
            doc.setAttributeValue("CUSTOM_ATTR", "This value is custom attribute value");
            doc.overrideAccessURL("path=" + file.getPath() + "&name="+file.getName());
        } catch (Exception e) {
            //handle errors
        }
    }

    protected void init()
    {
       super.init();
       if (!isLeaf())
       {
          File[] files = file.listFiles();

          if (files != null)
          {
             for (int i = 0; i < files.length; i++)
             {
                addChildNode(new CrawlableTreeNodeImpl(files[i].getAbsolutePath()));
             }
          }
       }
    }
    private static boolean isLink(File file) {
        try {
            if (!file.exists()) {
                return true;
            } else {
                String cnnpath = file.getCanonicalPath();
                String abspath = file.getAbsolutePath();
                return !abspath.equals(cnnpath);
            }
        } catch (IOException ex) {
            System.err.println(ex);
            return true;
        }
    }
    public boolean isLeaf() {
        return file == null "example" -->


In the processNode method, you must extract any metadata information about the document (for example, setTitle, setKeyword, setContent) and populate the indexable document that is passed in. You can also add any custom attributes to the indexable documents, such as the isLeaf method that determines whether a node is a folder or a document. This method is used by the crawlable factory to determine how to traverse the tree.

31.4.1.3 Extending AbstractTreeWalker

To provide the methods that enables Oracle SES to traverse tree structures and index the content items, extend AbstractTreeWalker by creating the abstract tree crawler class, called FileTreeCrawler, as shown in Example 31-6. FileTreeCrawler is invoked as a factory to create a crawlable tree node as defined in Section 31.4.1.2, "Implementing a Crawlable Tree Node". The tree walker works with CrawlableTreeNode to provide the generic framework for traversing a tree structure and indexing the content items in the repository. It also uses TreeSplitter to divide the repository into branches to enhance the crawling performance.

Example 31-6 Sample Code for FileTreeCrawler Class

package oracle.ecsf.data.tree;

import oracle.ecsf.meta.SearchableObject;

public class FileTreeCrawler extends AbstractTreeWalker {
    //
    //The application  determine the root path.
    //
    String root="/home/cbrown";
    /**
     *Constructs a CrawlableFactory from a searchable object.
     */
     public FileTreeCrawler()
     {
        super();
     }

    public FileTreeCrawler(SearchableObject searchableObject)
    {
       super(searchableObject);
    }
    //
    //Creates root node
    //
    public CrawlableTreeNode createCrawlable() {
        return new CrawlableTreeNodeImpl(root);
    }
    //
    //Creates a node for a given path
    //if you cannot construct the path.
    //
    public CrawlableTreeNode createCrawlable(String path) {
        return new CrawlableTreeNodeImpl(path);
    }
    //Tests if a node is crawlable
    protected boolean isIndexable(CrawlableTreeNode node) {
        CrawlableTreeNodeImpl fNode = (CrawlableTreeNodeImpl) node;
        return fNode.isIndexable();
    }
}

When Oracle SES crawls the searchable object, an instance of FileTreeCrawler is created by ECSF. It traverses the tree structure by calling the methods defined in the FileTreeCrawler class. It goes through two passes. First, it collects only the structure information, and based on that, it forms a control feed that contains all the folders that need to be visited for collecting documents. The isIndexable method determines whether or not a particular node is indexed by Oracle SES. You can also use it to place filters to control the type of document to be indexed.

When creating the FileTreeCrawler class, you must implement two constructors. One takes no parameters, and the other takes a searchable object where you can perform configurations specific to your application, if required.

31.4.1.4 Implementing Security

Security rules on the documents indexed by Oracle SES is controlled by access control lists (ACLs). This is achieved by creating a search plug-in that implements the oracle.ecsf.Secure interface for the searchable object. When you implement a security extension for the searchable object, it is used to serve as the authorization module for indexed content in Oracle SES.

Note:

ECSF uses the generic term ACL to describe how Oracle SES and ECSF pass security information and perform security checks by using the information described in the ACL.

ECSF is secured by a plugable security service, which is called when users try to search the indexed content. By default, ECSF provides an implementation based on Oracle Platform Security for Java. It is mainly used for authenticating and authorizing users into the system. However, if you have a non-Oracle security provider, or you want to use your own security implementation, you must extend the ECSF security service. Example 31-7 illustrates the skeleton of a security extension.

Example 31-7 Sample of Security Service

package oracle.ecsf.data.tree;

import oracle.ecsf.SearchContext;
import oracle.ecsf.SecurityService;
import oracle.ecsf.util.SecurityServiceFactory;

public class SecurityServiceImpl implements SecurityService{
    public String[] listSupportedFormats() {
        return new String[]{"BIEE"};
    }

    public String authenticate(SearchContext ctx, String userName, String password, String format) {
        return userName;
    }

    public String isUserValid(SearchContext ctx, String userName, String format) {
        return userName;
    }
    public String[] getSecurityValues(SearchContext ctx, String userName, String attrName, String objectId) {
        //Get keys for an attribute.
        return new String[]{};
    }
}

The security extension is a Java class that implements a securable interface. There are five methods available. Example 31-8 illustrates the skeleton of such class.

Example 31-8 Sample Security Extension

package oracle.ecsf.data.tree;

import oracle.ecsf.IndexableDocument;
import oracle.ecsf.SearchContext;
import oracle.ecsf.SearchSecurityException;
import oracle.ecsf.Securable;

public class SecurityPlugin implements Securable{
    public boolean isAclEnabled(SearchContext ctx) {
        return true;
    }
    //The ACL for the document
    public String[] getAcl(SearchContext ctx, IndexableDocument doc) {
        return new String[]{"cbrown"};
    }
    //The keys to the ACL for the document for ctx.getUserName()
    public String[] getSecurityKeys(SearchContext ctx) {
        return new String[]{};
    }
    //The ACL for the document, hashed against an attribute
    public String[] getSecureAttrAcl(SearchContext ctx, IndexableDocument doc, String attributeName) {
        return new String[]{};
    }
    //The keys to the ACL for the document for ctx.getUserName(), hashed agains an attribute
    public String[] getSecureAttrKeys(SearchContext ctx, String attributeName) {
        return new String[]{};
    }
    //Returns configuration parameters for the extension, not used often
    public String[] getSecurableParams()
       throws SearchSecurityException {
        return new String[]{};
    }
}

Your search plug-in must be assigned to the searchable object, as shown in Example 31-4.

31.4.1.5 Implementing the Attachments Interface

Implementing the attachments interface, shown in Example 31-9, allows you to index binary files such as Word documents, Excel spreadsheets, PDF files, and so on.

Example 31-9 Attachments Interface Implementation

package oracle.ecsf.data.tree;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;

import java.util.HashMap;
import java.util.Map;

import oracle.ecsf.Attachment;
import oracle.ecsf.SearchContext;
import oracle.ecsf.meta.PrimaryKey;

public class AttachmentImpl implements Attachment {
    PrimaryKey primaryKey;

    public AttachmentImpl() {
    }

    public AttachmentImpl(Map map) {
        primaryKey = new PrimaryKey();
        primaryKey.putAll(map);
    }

    public String getType() {
        return "text/xml";
    }

    public void initialize(SearchContext ctx, Map paramMap, PrimaryKey keys) {
        primaryKey = new PrimaryKey();
        primaryKey.putAll(keys);
    }

    public void read(SearchContext ctx, OutputStream stream) {
        String path = (String)primaryKey.get("Path");
        File file = new File(path);

        try {
            FileReader fileReader = new FileReader(file);
            BufferedReader br = new BufferedReader(fileReader);
            String strd = br.readLine();
            while (strd != null) {
                stream.write(strd.getBytes());
                strd = br.readLine();
            }
        } catch (IOException e) {
            //Handle errors
        }
    }
   //Contains configuration parameters needed to read the attachment
   //This map can be an empty one as shown in this example.
    public Map getParameters() {
        Map parameters = new HashMap();
        return parameters;
    }
   //Name value pairs need to identify a specific attachment.
    public PrimaryKey getPrimaryKey() {
        return primaryKey;
    }
}

Once you implement this class, you can add any number of attachments to an indexable document in the processNode method of CrawlableTreeNode. The attachment must contain enough information in its primary key for you to open the attachment when requested by the read method, where you simply use the information stored in the primary key to read the document and write to the output stream passed to you.

31.4.1.6 Deploying and Starting the ECSF Servlet

Before Oracle SES can crawl your file system, you need an Oracle WebLogic Server instance to which you can deploy the ECSF servlet. For example, you can use the Integrated WebLogic Server container.

To deploy and start the ECSF server:

  1. Create a Java project in JDeveloper.

  2. Add ecsf.jar to its class path.

  3. Develop your extensions.

  4. Edit web.xml to add the servlet mapping in Example 31-10.

    Example 31-10 SearchFeedServlet Mapping

    <servlet>
    <servlet-name>SearchFeedServlet</servlet-name>
    <servlet-class>oracle.ecsf.feed.SearchFeedServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>SearchFeedServlet</servlet-name>
    <url-pattern>/searchfeedservlet/*</url-pattern>
    </servlet-mapping>
    
  5. To use a custom security service, you must add -Doracle.ecsf.security.service=classnameOfSecurityService. Otherwise, Oracle Platform Security for Java security service is used.

  6. Open ecsf.jar, right-click oracle.ecsf.feed.SearchFeedServlet, and select Run. The ECSF servlet starts, and the system is ready to be crawled.

31.4.1.7 Configuring Oracle SES to Crawl ECSF

Oracle SES must be installed, then configured to crawl ECSF. Install Oracle SES 11.2.1, then perform the following steps to configure Oracle SES with the necessary information for crawling tree structure-based data sources and identifying items for indexing.

To configure Oracle SES:

  1. In Oracle SES, create a data source of data source type, for example, Oracle Fusion.

    For General, complete the following:

    • Name: your data source name

      Oracle SES supports string values of up to 100 characters.

    • Configuration URL: http://yourhost:port/appname/pathname/searchableObjectName/ConfigFeed

    • Authentication Type: NATIVE

    • User ID: username

    • Password: password

    • Scratch Directory: /tmp or c:\tmp or empty

    • Maximum number of connection attempts: 3

    For Authentication, complete the following:

    • Authorization: ACLs Controlled by the Source

    • HTTP endpoint for authorization: http://yourhost:port/appname/pathname/SecurityService?engineInstID=EngineInstanceID

    • User ID: username

    • Password: password

    • Business Component: searchableObjectName

    • Display URL Prefix: prefix of url for Oracle SES UI

  2. Create a data source group:

    1. From Search Tab/Source Group, click the Create button.

    2. Provide a name.

    3. Select Fusion Data Type to filter the data source.

    4. Shuttle the data source to the right column.

    5. Click Finish.

  3. Activate the identity extension:

    1. From Global Settings, select Identity Management Setup.

    2. Select Oracle Fusion from the list.

    3. Click Activate. If it is already activated, deactivate it, then reactivate it.

    4. For HTTP endpoint for authentication, enter http://yourhost:port/appname/pathname/SecurityService.

    5. Enter your user name and password.

  4. If Oracle SES is needed, create a federated trusted entity:

    1. From Global Settings, select Federation Trusted Entities.

    2. For Entity Name, enter your user name.

    3. Select the Use Identity Plug-in checkbox for the authentication option.

  5. (Optional) Include the Oracle SES client Java archive (JAR) in your class path, as shown in Example 31-11, to query Oracle SES through API.

    Example 31-11 Class Path with Oracle SES Client Java Archive

    public SearchHits doSearch(String soName, String query, int pageSize, int page)
       {
          SearchHits searchHits = null;
          String searchGroupName = GROUP_NAME;
          long engineInstId = -1;
          SearchContext searchContext = ContextFactory.getSearchContext();
         
          QueryMetaData qmd = new QueryMetaDataImpl();
          qmd.setQueryString(query);
          qmd.setPageSize(pageSize);
          qmd.setCurrentPage(page);
         
          SearchGroup group = new SearchGroup(searchGroupName, searchGroupName, -1, null, SearchContext.LOCAL);
          SearchGroup[] groups = new SearchGroup[] { group };
          qmd.setSearchGroups(groups);
     
          SearchableGroup sg = MetaDataManager.getSearchableGroup(-1, searchGroupName);
          sg.addSearchableObject(soName);
         
          SearchableObject so = MetaDataManager.getSearchableObject(soName);
          searchContext.setSearchableObject(so);
     
          SearchEngine engine = SearchEngineManager.getSearchEngine(engineInstId);
          try
          {
             Searcher searcher = engine.getSearcher(searchContext);
             searchHits = searcher.search(searchContext, qmd);
          }
          catch (Throwable e)
          {
             return null;
          }
          return searchHits;
       }
    

31.4.2 How to Integrate Search Functionality for Tree Structures

In the ECSF architecture, search related artifacts can be stored in any persistent storage. The metadata manager obtains these artifacts via configuration store abstraction. For Oracle Fusion Applications, a database-based configuration store is developed that is capable of loading configuration from the ECSF tables in the Oracle Fusion Applications database.

If you decide not to use the ECSF database-based configuration store for the metadata, you can implement your own configuration store by implementing the oracle.ecsf.meta.Configuration interface.

The search related artifacts are loaded into memory through VOConfiguration during runtime to be used for crawling, query, and administration. If information is not stored in a database, as in the case of Oracle Business Intelligence that stores its information in a tree structure-based source system, ECSF provides a flexible way to load the runtime objects through an interface (Configuration) placed between MetaDataManager and your configuration storage so that runtime objects are not restricted to being loaded from the database. Figure 31-5 illustrates the runtime architecture that includes the Configuration interface, which provides an alternative mechanism for loading runtime objects.

Figure 31-5 Runtime Architecture with Configuration Interface

Configuration interface

A system property determines which configuration, VOConfiguration or a custom configuration, to use during runtime. In Figure 31-5, the BIConfiguration class is an example of a custom configuration that extends the existing AbstractConfiguration class.

Integrate search functionality for tree structures by extending the AbstractConfiguration class and using your configuration class.

31.4.2.1 Setting the Configuration

The MetaDataManager class determines which configuration to call based on how you set the oracle.ecsf.configuration.class system property. For example,

System.setProperty("oracle.ecsf_configuration_class", "oracle.ecsf.meta.impl.BIConfiguration");

sets the property to use BIConfiguration. If this property is not set, or an implementation class does not exist for this property, MetaDataManager calls VOConfiguration by default.

31.4.2.2 Using the Configuration Interface

The Configuration interface, shown in Example 31-12, contains the methods implemented by VOConfiguration or a custom configuration to load runtime objects.

Example 31-12 Configuration Interface

public interface Configuration
{
   /**
    * Returns all search engine instances.
    * @return a list of search engine instances available.
    */
   public List<MetaEngineInstance> getEngineInstances();

   /**
    * Returns engine parameters in a hashmap for a given engine
    * instance.
    * @param engineId the engine instance id
    * @return Hashmap configuration parameter
    */
   public Map getEngineParameters(long engineId);

   /**
    * Returns a searchable group for a given search engine instance, by name.
    * @param engineId The identification of the engine instance.
    * @param name The name of the searchable group.
    * @return a searchable group. Null if not found.
    */
   public SearchableGroup getSearchableGroup(long engineId, String name);

   /**
    * Returns a searchable object for a given search engine instance by class
    * name.
    * @param engineId The identification of the engine instance.
    * @param name The class name of the searchable object.
    * @return a searchable object, null if not found.
    */
   public SearchableObject getSearchableObject(long engineId, String name);

   /**
    * Returns a list of searchable groups for a given search engine instance.
    * @param engineId The indentification of the engine instance.
    * @return a list of searchable groups, empty if not found.
    */
   public List<SearchableGroup> getSearchableGroups(long engineId);

   /**
    * Requests a reload of the configuration. Implementation should reload the
    * objects from persistent storage.
    */
   public void reload();
}

The Configuration interface includes the getEngineParameters() method so you can get and set the search engine parameters needed for ECSF runtime in the absence of a database.

31.4.2.3 Using the AbstractConfiguration Class

The AbstractConfiguration class implements the necessary functionalities common to all non-database uptakers regardless of where the runtime object information is stored. You must complete the implementation by using your own custom class that extends AbstractConfiguration. For information, see Section 31.4.2.5, "Extending AbstractConfiguration".

Example 31-13 illustrates the implementation of AbstractConfiguration. In this implementation, getSearchableGroups() loads the available groups from the Oracle SES instance. These groups are treated as external groups, and therefore advanced search and facets are not supported in this scenario.

Example 31-13 Sample AbstractConfiguration Implementation

public abstract class AbstractConfiguration implements Configuration
{
   public AbstractConfiguration()
   {
      super();
      MetaEngineInstance engine = new MetaEngineInstance();
      engine.setId(-1L);
      engines.add(engine);

   }

   public List<MetaEngineInstance> getEngineInstances()
   {
      return engines;
   }
   
   public List<SearchableGroup> getSearchableGroups(long engineId)
   {
      if(groups == null)
      {
         groups = new ArrayList<SearchableGroup>();
         try
         {
            Map map = SESAdmin.getGroups(ContextFactory.getSearchContext(), engineId);
            for (Object key : map.keySet())
            {
               SearchableGroup sg = new SearchableGroup(key.toString());
               sg.setDisplayName((String)map.get(key));
               sg.setIsExternal(true);
               sg.setEngineInstanceId(engineId);
               groups.add(sg);
            }
         }
         catch (SearchException e)
         {
            e.printStackTrace();
         }
      }
      return groups;
   }

   public SearchableGroup getSearchableGroup(long engineId, String s)
   {
      for (SearchableGroup group : getSearchableGroups(engineId))
      {
         if (group.getName().equals(s))
         {
            return group;
         }
      }
      return null;
   } 
   
   public SearchableObject getSearchableObject(long engineId, String s)
   {
      SearchableObject so = loadSOFromClass(s);
      if (so != null)
      {
         so.setSearchEngineInstanceId(engineId);
      }
      return so;
   }
   
   protected SearchableObject loadSOFromClass(String className)
   {
      try
      {
         if (className.equals(SearchableObject.class.getName()))
         {
            return null;
         }

         Class cls =
            Thread.currentThread().getContextClassLoader().loadClass(className);
         if (cls != null)
         {
            Object object = null;
            try
            {
               object = cls.newInstance();
            }
            catch (InstantiationException e)
            {

               return null;
            }
            catch (IllegalAccessException e)
            {

               return null;
            }

            if (object instanceof SearchableObject)
            {
               //this is the only path a searchable object will be created
               SearchableObject so = (SearchableObject)object;
               so.setDocument(new VODocumentImpl("root"));
               return so;
            }
            else
            {
               return null;
            }
         }
         else
         {
            return null;
         }
      }
      catch (ClassNotFoundException e)
      {
         return null;
      }
   }
   
   public void reload()
   {
      //do nothing
   }

   private List<SearchableGroup> groups = null;
   private List<MetaEngineInstance> engines= new ArrayList<MetaEngineInstance>();
}

In this implementation, there is only one engine instance but following MetaDataManager convention, a list of engine instances is returned.

31.4.2.4 Implementing Searchable Object Classes

Since the searchable objects are not stored in the database, you must implement a Java class to define each searchable object. Example 31-14 shows the sample implementation for EmpView.java.

Example 31-14 Sample Searchable Object Class

package runtime;  //package where runtime objects reside
import java.util.logging.Logger;

import oracle.ecsf.meta.AbstractDocumentDefinition;
import oracle.ecsf.meta.DocumentDefinition;
import oracle.ecsf.meta.FieldDefinitionImpl;
import oracle.ecsf.meta.SearchableObject;
import oracle.ecsf.util.ECSFLoggerFactory;

public class EmpView extends SearchableObject
{
   private static Logger sLogger =
      ECSFLoggerFactory.getLogger(EmpView.class.getName());

   private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

   public EmpView()
   {
      super("");
      setName(getClass().getName());
   }

   public EmpView(String name)
   {
      super(name);
   }

   /**
    * Override to initalize document.
    * @param document
    */
   public final void setDocument(AbstractDocumentDefinition document)
   {
      super.setDocument(document);
      initDoc(document);
   }
   private void initDoc(AbstractDocumentDefinition documentDefintion)
   {
      FieldDefinitionImpl field = new FieldDefinitionImpl("Ename");
      field.setBinding("ENAME");
      field.setPrimaryKey(false);
      field.setStored(true);
      documentDefintion.addField(field);
      field = new FieldDefinitionImpl("Empno");
      field.setBinding("EMPNO");
      field.setPrimaryKey(true);
      field.setStored(true);
      documentDefintion.addField(field);
      field = new FieldDefinitionImpl(DocumentDefinition.ECSF_SO_NAME);
      field.setBinding(DocumentDefinition.ECSF_SO_NAME);
      field.setPrimaryKey(false);
      field.setStored(true);
      documentDefintion.addField(field);        
   }    
}

Each searchable object is loaded using a class of the same name.

31.4.2.5 Extending AbstractConfiguration

You can extend the AbstractConfiguration class with the BIConfiguration class, as shown in Example 31-15, to define your own way of loading runtime objects that is specific to your environment.

Example 31-15 Sample Implementation for BIConfiguration

package oracle.ecsf.meta.impl;

import java.util.HashMap;
import java.util.Map;


public class BIConfiguration extends AbstractConfiguration
{
   public BIConfiguration()
   {
   }
   public Map getEngineParameters(long engineId)
   {
      HashMap map = new HashMap();
      map.put("SES_ADMIN_USERNAME", "searchsys");
      map.put("SES_ADMIN_PASSWORD", "welcome1");
      map.put("SES_ADMIN_SESSION_TIMEOUT", "10");
      map.put("SES_ADMIN_SERVICE",

"http://sesserver.com:7777/search/api/admin/AdminService");
      map.put("SES_QUERY_SERVICE",

"http://sesserver.com:7777/search/query/OracleSearch");
      map.put("SES_QUERY_PROXY_USERNAME", "scott");
      map.put("SES_QUERY_PROXY_PASSWORD", "tiger");
      map.put("SES_QUERY_SESSION_TIMEOUT", "10");
      map.put("ECSF_DATA_SERVICE",

"http://wlsserver.com:7101/approot/searchfeedservlet/");
      map.put("ECSF_SECURITY_USERNAME", "scott");
      map.put("ECSF_SECURITY_PASSWORD", "tiger");
      map.put("ECSF_SECURITY_SERVICE",

"http://wlsserver.com:7101/approot/searchfeedservlet/");
      map.put("ECSF_REDIRECT_SERVICE",

"http://wlsserver.com:7101/approot/searchfeedservlet/");
      return map;
   }

  //add search group and search object used for SESAdmin
   public List<SearchableGroup> getSearchableGroups(long engineId)
   {
      if(searchGroups == null)
      {
         searchGroups = new ArrayList(super.getSearchableGroups(engineId));   
         SearchableGroup sgAdmin = new SearchableGroup(GROUP_NAME_ADMIN);
         sgAdmin.setIsExternal(false);
         sgAdmin.setEngineInstanceId(ENGINE_ID);
         SearchableObject so = super.loadSOFromClass(OBJECT_NAME_ADMIN);
         sgAdmin.addSearchableObject(so);
         searchGroups.add(sgAdmin);
      }
      return searchGroups;
   }
   
   private List<SearchableGroup> searchGroups = null;

   private static final long ENGINE_ID = -1L;
   private static final String GROUP_NAME_ADMIN = "runtime.EmpViewAdminTest";
   private static final String OBJECT_NAME_ADMIN = "runtime.EmpViewAdminTest";
   
}

31.5 Setting Up Federated Search

ECSF provides the services and federation to enable users to search across Oracle Fusion Applications product families or across multiple Oracle SES instances. For more information, see the "Managing Search with Oracle Enterprise Crawl and Search Framework" chapter in the Oracle Fusion Applications Administrator's Guide.

Set up ECSF services and federation by:

  1. Creating the SearchDB connection on Oracle WebLogic Server

  2. Updating the application deployment profile with the Target Directory for Searchable Objects

  3. Updating the application to point to the ECSF Service shared library

  4. Adding the ECSF Runtime Library

  5. Adding searchable objects and their dependencies to the Search application

  6. Setting the system parameter for web service

  7. Packaging and deploying the Search application

  8. Setting up the ECSF client for federation

  9. Setting the SearchContext scope to GLOBAL

  10. Integrating federation across Oracle Fusion Applications product families

31.5.1 How to Create the SearchDB Connection on Oracle WebLogic Server Instance

The Oracle WebLogic Server instance to which the application is deployed must have a SearchDB connection. The application deployment descriptors are set so that the application does not automatically generate and synchronize weblogic-jdbc.xml descriptors during deployment. This setting prevents you from receiving the deployment error No credential mapper entry found for password indirection when you package or deploy from the command line or from Oracle JDeveloper. Because of this, you must manually create the SearchDB connection on Oracle WebLogic Server instance.

To create the SearchDB connection:

  1. In the Domain Structure tree of the Oracle WebLogic Server Administration Console, navigate to Services, then JDBC, then Data Sources.

  2. See if there is any data source with Java Naming and Directory Interface (JNDI) value jdbc/SearchDBDS. If not, proceed to the next step.

  3. Click the New.

  4. On the Connection Properties page, complete the fields with the following values:

    • JNDI Name: jdbc/SearchDBDS

    • Database Type: Oracle

    • Database Driver: Oracle Driver (Thin) 901,92,10,11

  5. Click Next, and complete the configuration according to your data source.

31.5.2 How to Update the Application Deployment Profile with the Target Directory for Searchable Objects

All searchable objects and their dependencies must be packaged within the Search application enterprise archive (EAR) file for each product family. You must set the target directory for the Java archive (JAR) files containing the searchable objects and their dependencies in the application deployment descriptor so that they are packaged with the enterprise archive.

To set the target directory for searchable objects:

  1. In the Application Navigator, right-click the application name and navigate to Application Properties, then Deployment.

    The deployment descriptor file (for example, Search_Application1.ear, where Search is your application name) appears in the left pane.

  2. Click the deployment descriptor file and select Edit.

  3. From the Edit EAR Deployment Profile Properties menu navigate to File Groups > VOLib > Contributors, click Add, and set the directory to point to {FULL_PATH_TO_BUILD_FILE}/deploy/lib/, for example, /ade/view_name/fusionapps/crm/deploy/lib.

    This directory is empty, but during the pre-enterprise archive step in the build file the directory becomes populated with all of the dependent Java archive (JAR) files. Including this directory in the application deployment descriptor ensures that these dependent Java archive (JAR) files are packaged with the enterprise archive.

  4. Click OK.

31.5.3 How to Update the Application to Reference the ECSF Service Shared Library

The ECSF Service shared library eliminates the need for ECSF libraries to be packaged into the Search application for each product family. Instead, applications that depend on ECSF libraries can reference the ECSF shared library that is deployed to the Oracle WebLogic Server instance. The ECSF Service shared library contains the following Java archive (JAR) files:

  • ecsf_MiddleTier.war

  • ecsf_MiddleTier.jar

  • ecsf_Common.jar

You must update the Oracle WebLogic Server deployment descriptor file (weblogic-application.xml) by adding the reference to the ECSF Service shared library (oracle.ecsf.service), as shown in Example 31-16.

Example 31-16 Reference to the ECSF Service Shared Library

<library-ref> 
      <library-name>oracle.ecsf.service</library-name>
</library-ref>

<library-context-root-override>
      <context-root>searchservice</context-root>
      <override-value>REPLACE _CONTEXT_ROOT</override-value>
</library-context-root-override>

Replace REPLACE _CONTEXT_ROOT with the context root that is desired for the Search application's ECSF Service.

The ECSF Service shared library is automatically deployed to the Integrated WebLogic Server instance by the ECSF extension in JDeveloper through the JDeveloper Application Development Runtime Service (ADRS). However, you must manually deploy the ECSF Service shared library to the standalone Oracle WebLogic Server.

31.5.4 How to Add the ECSF Runtime Library

After you update the application deployment profile, update the project by adding the ECSF Runtime Library.

To add the ECSF Runtime Library:

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

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

  3. In the Add Library dialog, select ECSF Runtime Server from the list of available libraries.

  4. Click OK to save your selection and close the Add Library dialog.

31.5.5 How to Set the System Parameter for Web Service

Set the oracle.ecsf.service.ws.timeout system parameter to specify the web service timeout value in milliseconds. If no value is specified, then 90,000 milliseconds is used. You can set the system parameter in either the Java system properties or in the ecsf.properties file.

31.5.5.1 Setting the System Parameter in Java System Properties

Set the oracle.ecsf.service.ws.timeout system parameter in Java System Properties to specify the web service timeout value.

To set the system parameter using Java system properties:

  1. In the Application Navigator, right-click ViewController and select Project Properties.

  2. In the Project Properties dialog, select Run/Debug/Profile in the left panel.

  3. Select Default in the Run Configurations list, then click the Edit button.

  4. In the Edit Run Configuration dialog, select Launch Settings in the left panel.

  5. Enter -Doracle.ecsf.service.ws.timeout=n (where n is the desired value in milliseconds) in the Java Options field, then click OK.

  6. Click OK.

31.5.5.2 Setting the System Parameter in the ecsf.properties File

Set the oracle.ecsf.service.ws.timeout system parameter in the ecsf.properties file to specify the web service timeout value by adding the following line to the ecsf.properties file available in the application classpath:

oracle.ecsf.service.ws.timeout=n

where n is the desired value in milliseconds.

31.5.6 How to Package and Deploy the Search Application

When the application deployment descriptor points to the right directory you can run the ant targets to package and deploy the EAR file. You can run the ant targets from the command line or from Oracle JDeveloper.

31.5.6.1 Running the ant Targets from the Command Line

Package and deploy the Search application by issuing the following commands in the directory where the build file is located:

ant -f build-crmsearch.xml ear

ant -Ddeployenvfile=/scratch/deploy.xml -f build-crmsearch.xml deploy

where build-crmsearch.xml is the build file for the Search application.

The enterprise archive step in the build file includes the pre-enterprise archive step, so there is no need to manually run the pre-enterprise archive step. The enterprise archive target also runs a postenterprise archive step that deletes all the files from the deploy/lib directory after the enterprise archive is packaged, resulting in a clean folder.

31.5.6.2 Running the ant Targets from Oracle JDeveloper

You can use Oracle JDeveloper to package and deploy the Search application.

To package and deploy the Search application from Oracle JDeveloper:

  1. Run the pre-enterprise archive steps by opening the build file, right-clicking the open file editor, and selecting Run ant target > lrg - writeGraph > pre-ear.

    This step runs the pre-enterprise archive steps that are required for copying required Java archive (JAR) files to the directory specified in the Search application deployment descriptor. All the files in that directory is packaged into the enterprise archive file in the APP-INF/lib file where the ECSF WAR can locate them.

  2. Package the enterprise archive by right-clicking the application and selecting Deploy > To ear.

  3. Deploy the enterprise archive by right-clicking the application and selecting Deploy > To IntegratedWebLogicServer.

31.5.7 How to Update the Search Application with New Searchable Objects or Dependencies

The Search application must be updated if there are new searchable objects to add to the application or if any of the dependencies for existing searchable objects change.

If there are no new searchable objects but dependencies have changed, you only need to run the dependentJar ant target and package and deploy the enterprise archive (for information, see Section 31.5.6, "How to Package and Deploy the Search Application").

However, if you are adding new searchable objects to the application, then you must add the new searchable objects to the Search application build file and run the dependentJar ant target, then repackage and redeploy the enterprise archive (for information, see Section 31.5.6, "How to Package and Deploy the Search Application").

31.5.8 How to Set Up the ECSF Client Application for Federation

In order to connect to the Search application for each Oracle Fusion Applications product family (collectively called global search applications), you must configure the client application so that when it sends the Search application server a request, it also sends valid encrypted proxy user credentials to the server.

The client that calls the Search application must be configured with information on where to find the global Search applications. This information is stored in the connections.xml of the client application. The connections.xml file of the client application must contain a reference name element corresponding to each remote ECSF component to which the client application connects.

Set up the ECSF client application by:

  1. Adding encryption keys to cwallet.sso and default-keystore.jks

  2. Adding the keystore to jps-config.xml

  3. Creating the proxy user

  4. Updating connections.xml

31.5.8.1 Adding Encryption Keys to cwallet.sso and default-keystore.jks

The security header is encrypted before being sent to the server, so the cwallet and default-keystore files for both the client and server must be configured for the encryption to function properly. Use the Oracle Weblogic Scripting Tool to create the encryption keys in cwallet.sso and default-keystore.jks. For more information, see Oracle Fusion Middleware Oracle WebLogic Scripting Tool.

The following four new entries appear in cwallet.sso and default-keystore.jks:

> createCred(map="oracle.wsm.security", key="keystore-csf-key", user="owsm", password="welcome1", desc="Keystore key")
> createCred(map="oracle.wsm.security", key="enc-csf-key", user="orakey", password="welcome1", desc="Encryption key")
> createCred(map="oracle.wsm.security", key="sign-csf-key", user="orakey", password="welcome1", desc="Signing key")
> createCred(map="oracle.wsm.security", key="basic.credentials", user="weblogic", password="weblogic1", desc="User credentials key")

Oracle Weblogic Scripting Tool updates the files directly on the server you specify, so no redeployment is necessary.

31.5.8.2 Adding the Keystore to jps-config.xml

In order for the client to be able to use the encryption keystore entries, you must configure the jps-config.xml file for the client application.

Add the following entries to jps-config.xml:

  • Under serviceProviders:

    <serviceProvider type="KEY_STORE" name="keystore.provider"
            class="oracle.security.jps.internal.keystore.KeyStoreProvider">
    </serviceProvider>
    
  • Under serviceInstances:

    <serviceInstance name="keystore" provider="keystore.provider" location="./default-keystore.jks">
            <description>Default JPS Keystore Service</description>
            <property name="keystore.type" value="JKS"/>
            <property name="keystore.csf.map" value="oracle.wsm.security"/>
            <property name="keystore.pass.csf.key" value="keystore-csf-key"/>
            <property name="keystore.sig.csf.key" value="sign-csf-key"/>
            <property name="keystore.enc.csf.key" value="enc-csf-key"/>
    </serviceInstance>
    
  • Under the default jpsContext:

    <serviceInstanceRef ref="keystore"/>
    

For more information, see Oracle Fusion Middleware Oracle WebLogic Scripting Tool.

31.5.8.3 Creating the Proxy User

The proxy user must exist on both the client and server. The client's cwallet.sso must also include an entry for the proxy user so that the username and password can be encrypted when they are placed in the security header before being sent to the server. Use Oracle Weblogic Scripting Tool to create an entry for the user in cwallet.sso, as shown in Example 31-17.

Example 31-17 Sample Proxy User Entry

createCred(map="oracle.wsm.security", key="test.user", user="weblogic", password="weblogic1", desc="User credentials key")

The key (in this example, test.user) for the new entry is used in connections.xml.

31.5.8.4 Updating connections.xml

The connection between the ECSF client and each of the remote ECSF service components is defined in the connections.xml file in the ECSF client. The connections.xml file contains a list of reference name elements that correspond to each ECSF service component. You must edit the connections.xml file to define the application server connection parameters.

To define the connection parameters:

  1. Expand Application Resources, then Descriptors, then ADF Meta-INF, and open the connections.xml file.

  2. Add the reference name elements, as shown in Example 31-18.

    Example 31-18 Sample Reference Element

    <Reference name="{/oracle/ecsf/service/query/common/crm/}SearchService" className="oracle.adf.model.connection.webservice.impl.WebServiceConnectionImpl" xmlns="">
          <Factory className="oracle.adf.model.connection.webservice.api.WebServiceConnectionFactory"/>
          <RefAddresses>
             <XmlRefAddr addrType="WebServiceConnection">
                <Contents>
                   <wsconnection description="http://localhost:7101/CrmSearchService/AppModuleSearchService?WSDL" service="{/oracle/ecsf/service/query/common/}AppModuleSearchService">
                      <model name="{/oracle/ecsf/service/query/common/}AppModuleSearchService" xmlns="http://example.com/ws/model">
                         <service name="{/oracle/ecsf/service/query/common/}AppModuleSearchService">
                            <port name="AppModuleSearchServiceSoapHttpPort" binding="{/oracle/ecsf/service/query/common/}AppModuleSearchServiceSoapHttp">
                               <soap addressUrl="http://localhost:7101/CrmSearchService/AppModuleSearchService" xmlns="http://schemas.xmlsoap.org/wsdl/soap/"/>
                            </port>
                         </service>
                      </model>
                   </wsconnection>
                </Contents>
             </XmlRefAddr>
          </RefAddresses>
       </Reference>
     <Reference name="{/oracle/ecsf/service/query/common/hcm/}SearchService" className="oracle.adf.model.connection.webservice.impl.WebServiceConnectionImpl" xmlns="">
          <Factory className="oracle.adf.model.connection.webservice.api.WebServiceConnectionFactory"/>
          <RefAddresses>
             <XmlRefAddr addrType="WebServiceConnection">
                <Contents>
                   <wsconnection description="http://localhost:7101/HcmSearchService/AppModuleSearchService?WSDL" service="{/oracle/ecsf/service/query/common/}AppModuleSearchService">
                      <model name="{/oracle/ecsf/service/query/common/}AppModuleSearchService" xmlns="http://example.com/ws/model">
                         <service name="{/oracle/ecsf/service/query/common/}AppModuleSearchService">
                            <port name="AppModuleSearchServiceSoapHttpPort" binding="{/oracle/ecsf/service/query/common/}AppModuleSearchServiceSoapHttp">
                               <soap addressUrl="http://localhost:7101/HcmSearchService/AppModuleSearchService" xmlns="http://schemas.xmlsoap.org/wsdl/soap/"/>
                            </port>
                         </service>
                      </model>
                   </wsconnection>
                </Contents>
             </XmlRefAddr>
          </RefAddresses>
       </Reference>
     <Reference name="{/oracle/ecsf/service/query/common/fscm/}SearchService" className="oracle.adf.model.connection.webservice.impl.WebServiceConnectionImpl" xmlns="">
          <Factory className="oracle.adf.model.connection.webservice.api.WebServiceConnectionFactory"/>
          <RefAddresses>
             <XmlRefAddr addrType="WebServiceConnection">
                <Contents>
                   <wsconnection description="http://localhost:7101/FscmSearchService/AppModuleSearchService?WSDL" service="{/oracle/ecsf/service/query/common/}AppModuleSearchService">
                      <model name="{/oracle/ecsf/service/query/common/}AppModuleSearchService" xmlns="http://example.com/ws/model">
                         <service name="{/oracle/ecsf/service/query/common/}AppModuleSearchService">
                            <port name="AppModuleSearchServiceSoapHttpPort" binding="{/oracle/ecsf/service/query/common/}AppModuleSearchServiceSoapHttp">
                               <soap addressUrl="http://localhost:7101/FscmSearchService/AppModuleSearchService" xmlns="http://schemas.xmlsoap.org/wsdl/soap/"/>
                            </port>
                         </service>
                      </model>
                   </wsconnection>
                </Contents>
             </XmlRefAddr>
          </RefAddresses>
     </Reference>
    

    Web service security must be enforced by policy at the domain or instance level by configuration. For information, see Oracle Fusion Middleware Oracle WebLogic Server Administration Console Online Help.

  3. Save.

  4. Redeploy the client application.

31.5.9 How to Set the SearchContext Scope to GLOBAL

ECSF Query APIs can either invoke the ECSF service component internally (GLOBAL) or locally (LOCAL). In order to set the ECSF calls to be routed to the ECSF service component, you must set the scope of the search context to GLOBAL, as shown in Example 31-19.

Example 31-19 Client Methods

SearchContext ctx = ContextFactory.getSearchContext();
ctx.setScope("GLOBAL");
  ArrayList<SearchEngineInstance> engineInstances = (ArrayList<SearchEngineInstance>)searchCtrl.getEngineInstances();

When the SearchContext scope is set to GLOBAL, the parameters defined for the remote engine instance in the database are used to access metadata objects and perform query related functions on the remote engine instance. For more information, see the "Managing Search with Oracle Enterprise Crawl and Search Framework" chapter in the Oracle Fusion Applications Administrator's Guide.

31.5.10 How to Integrate Federation Across Oracle Fusion Applications Product Families

Use the ECSF API, as shown in Example 31-20, to integrate federation across Oracle Fusion Applications product families.

Example 31-20 Sample API for Implementing Federated Search

SearchCtrl searchCtrl = new SearchCtrl();
      SearchHits searchHits = null;     
      QueryMetaDataImpl queryMetaData = new QueryMetaDataImpl();
      queryMetaData.setQueryString("*");
      queryMetaData.setPageSize(10);
      queryMetaData.setCurrentPage(1);
      ctx.setScope(SearchContext.GLOBAL); 
      ctx.setCurrLocale(Locale.US);

      ArrayList<SearchGroup> allGroups = new ArrayList<SearchGroup>();
      ArrayList<SearchGroup> searchGroups = new ArrayList<SearchGroup>();
      
      ArrayList<SearchEngineInstance> engineInstances =
         (ArrayList<SearchEngineInstance>)searchCtrl.getEngineInstances();
      for (SearchEngineInstance engineInstance : engineInstances)
      {
         allGroups.addAll(engineInstance.getSearchGroups());
      }
      for (SearchGroup searchGroup : allGroups)
      {
         if (searchGroup.getName().equals("runtime.EmpView") 
|| searchGroup.getName().equals("Service Request"))
         {
            searchGroups.add(searchGroup);
         }
      }
      SearchGroup sgs[] = searchGroups.toArray(new SearchGroup[searchGroups.size()]);
      queryMetaData.setSearchGroups(sgs);
      try
      {
         searchHits = searchCtrl.runQuery(ctx, queryMetaData);
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }

In the example, ECSF runtime gets the runtime.EmpView search category (search group) from an engine instance.

31.6 Federating Oracle SES Instances

ECSF supports federation across Oracle SES instances, which allows users to query across multiple Oracle SES instances defined in the client's own ECSF component. In Figure 31-6, the ECSF client is connected to one ECSF service component, which depends on one database and two Oracle SES instances.

Figure 31-6 Federated Oracle SES

Federated SES

Federation occurs on the client through the Searcher class. For each Oracle SES instance, ECSF runtime groups the search categories belonging to an Oracle SES instance and creates a federation node for it. Separate queries are issued in separate threads for each Oracle SES instance. The results from these queries are merged by ECSF runtime and returned to the user.

Note:

You cannot federate both Oracle SES instances and ECSF service components in the same query. ECSF runtime can only create federation nodes for either each Oracle SES instance (for federated Oracle SES) or each ECSF service component (for federated search).

Example 31-21 illustrates how you can integrate federation across Oracle SES instances.

Example 31-21 Sample API for Integrating Federated Oracle SES

SearchContext ctx = ContextFactory.getSearchContext();
ctx.setScope(SearchContext.LOCAL);
SearchGroup[] sgs =
   new SearchGroup[] { new SearchGroup("runtime.EmpView", "runtime.EmpView", 1), 
                 new SearchGroup("runtime.EmpView", "runtime.EmpView", 17) };

queryMetaData.setSearchGroups(sgs);
try
{
   searchHits = searchCtrl.runQuery(ctx, queryMetaData);
}
catch (Exception e)
{
   bException = true;
}

In the example, two Oracle SES search engine instances are defined.

31.7 Raising Change Events Synchronously

You can use a Java API to raise change events synchronously with asynch flag = false when searchable object records are modified. Raising events using SQL writes records to the ECSF_SEARCHABLE_CHANGE_LOG table of the database.

You can raise change events synchronously by implementing code like the sample code in Example 31-22.

Example 31-22 Sample Code for Raising Events Using SQL

SearchEventInvocation searchEventInvocation = new SearchEventInvocation();
PrimaryKey primaryKey = new PrimaryKey();
primaryKey.put("PartyId", "12322");
SearchChangeLogEvent changeEvent = new SearchChangeLogEvent(pk);
changeEvent.setSearchObjectName("oracle.ecsf.search.demo.EmpVO");
changeEvent.setChangeType(IndexableDocument.INSERT);
searchEventInvocation.raiseEvent(changeEvent, false);

You can use the following ChangeType parameters:

  • IndexableDocument.DELETE

  • IndexableDocument.UPDATE

  • IndexableDocument.INSERT

When using the SearchEventInvocation.raiseEvent(PrimaryKey eventPK, boolean useFabric) method to create change log records, the keys in the eventPK map must be aliases of view object attributes. A view object's alias can be found by opening the view object in JDeveloper, or by calling the getBinding() method of the appropriate oracle.ecsf.meta.FieldDefinition instance. Also, the eventPK map must contain an entry for every attribute that is part the primary key makeup. Otherwise, the raiseEvent method throws an exception.

For more information, see the Javadoc for ECSF.

31.8 Using the External ECSF Web Service for Integration

In addition to Java APIs, Oracle Enterprise Crawl and Search Framework (ECSF) provides an external web service for you to integrate ECSF with Oracle Fusion Applications. This web service allows you to build a custom search user interface that enables Oracle Fusion Applications users to search across multiple ECSF service components through a web service client. As an alternative to using Java APIs, you can invoke the external ECSF web service from Oracle Fusion Applications to perform query related functions in both LOCAL and GLOBAL scopes. Using a web service client, users can query across multiple Oracle SES instances in LOCAL scope or across multiple ECSF service components in GLOBAL scope.

31.8.1 Web Service Methods

The external ECSF web service reuses the web service already provided by an ECSF component and exposes the methods described in Table 31-1.

Table 31-1 ECSF Web Service Methods

Method Description

getSavedSearch(String userName, String savedSearchRequest)

Returns a saved search based on the name passed in to the savedSearchRequest parameter.

getSavedSearches(String userName, String savedSearchRequest)

Returns the saved searches based on the caller context passed in to the savedSearchRequest parameter.

saveSearch(String userName, String savedSearchRequest)

Saves the search passed in to the savedSearchRequest parameter.

deleteSearch(String userName, String savedSearchRequest)

Deletes the saved search passed in to the savedSearchRequest parameter.

getSavedSearchDetails(String userName, String savedSearchRequest)

Returns the saved search details based on the saved search passed in to the savedSearchRequest parameter.

search(String userName, String queryMetaDataRequest)

Returns search hits based on the request passed in to the QueryMetaData parameter.

getEngineInstances(String userName, String engineInstanceRequest)

Returns the engine instances based on the engine type ID passed in to the engineInstanceRequest parameter.


31.8.2 ECSF Web Service WSDL and XSD

The Web Service Description Language (WSDL), shown in Example 31-23, defines the message endpoints and the request and reply messages of the ECSF web service. The XSD, shown in Example 31-24, defines the XML schema of the ECSF web service. Refer to the WSDL and XSD to understand and interact with the ECSF web service.

Example 31-23 ECSF Web Service WSDL

<wsdl:definitions
     name="AppModuleSearchService"
     targetNamespace="/oracle/ecsf/service/query/common/"
     xmlns:errors="http://xmlns.example.com/adf/svc/errors/"
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
     xmlns:tns="/oracle/ecsf/service/query/common/"
     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
     xmlns:types="/oracle/ecsf/service/query/common/types/">
    <wsdl:import namespace="http://xmlns.example.com/adf/svc/errors/" location="classpath:/META-INF/wsdl/ServiceException.wsdl"/>
    <wsdl:types>
        <schema xmlns="http://www.w3.org/2001/XMLSchema">
            <import namespace="/oracle/ecsf/service/query/common/types/" schemaLocation="AppModuleSearchService.xsd"/>
        </schema>
    </wsdl:types>
    <wsdl:message name="AppModuleSearchService_search">
        <wsdl:part name="parameters" element="types:search"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_searchResponse">
        <wsdl:part name="parameters" element="types:searchResponse"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_saveSearch">
        <wsdl:part name="parameters" element="types:saveSearch"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_saveSearchResponse">
        <wsdl:part name="parameters" element="types:saveSearchResponse"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getSavedSearches">
        <wsdl:part name="parameters" element="types:getSavedSearches"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getSavedSearchesResponse">
        <wsdl:part name="parameters" element="types:getSavedSearchesResponse"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getSavedSearchDetails">
        <wsdl:part name="parameters" element="types:getSavedSearchDetails"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getSavedSearchDetailsResponse">
        <wsdl:part name="parameters" element="types:getSavedSearchDetailsResponse"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getSavedSearch">
        <wsdl:part name="parameters" element="types:getSavedSearch"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getSavedSearchResponse">
        <wsdl:part name="parameters" element="types:getSavedSearchResponse"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getEngineInstances">
        <wsdl:part name="parameters" element="types:getEngineInstances"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getEngineInstancesResponse">
        <wsdl:part name="parameters" element="types:getEngineInstancesResponse"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getDisplayName">
        <wsdl:part name="parameters" element="types:getDisplayName"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_getDisplayNameResponse">
        <wsdl:part name="parameters" element="types:getDisplayNameResponse"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_deleteSearch">
        <wsdl:part name="parameters" element="types:deleteSearch"/>
    </wsdl:message>
    <wsdl:message name="AppModuleSearchService_deleteSearchResponse">
        <wsdl:part name="parameters" element="types:deleteSearchResponse"/>
    </wsdl:message>
    <wsdl:portType name="AppModuleSearchService">
        <wsdl:documentation/>
        <wsdl:operation name="search">
            <wsdl:input message="tns:AppModuleSearchService_search"/>
            <wsdl:output message="tns:AppModuleSearchService_searchResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
        <wsdl:operation name="saveSearch">
            <wsdl:input message="tns:AppModuleSearchService_saveSearch"/>
            <wsdl:output message="tns:AppModuleSearchService_saveSearchResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
        <wsdl:operation name="getSavedSearches">
            <wsdl:input message="tns:AppModuleSearchService_getSavedSearches"/>
            <wsdl:output message="tns:AppModuleSearchService_getSavedSearchesResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
        <wsdl:operation name="getSavedSearchDetails">
            <wsdl:input message="tns:AppModuleSearchService_getSavedSearchDetails"/>
            <wsdl:output message="tns:AppModuleSearchService_getSavedSearchDetailsResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
        <wsdl:operation name="getSavedSearch">
            <wsdl:input message="tns:AppModuleSearchService_getSavedSearch"/>
            <wsdl:output message="tns:AppModuleSearchService_getSavedSearchResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
        <wsdl:operation name="getEngineInstances">
            <wsdl:input message="tns:AppModuleSearchService_getEngineInstances"/>
            <wsdl:output message="tns:AppModuleSearchService_getEngineInstancesResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
        <wsdl:operation name="getDisplayName">
            <wsdl:input message="tns:AppModuleSearchService_getDisplayName"/>
            <wsdl:output message="tns:AppModuleSearchService_getDisplayNameResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
        <wsdl:operation name="deleteSearch">
            <wsdl:input message="tns:AppModuleSearchService_deleteSearch"/>
            <wsdl:output message="tns:AppModuleSearchService_deleteSearchResponse"/>
            <wsdl:fault name="ServiceException" message="errors:ServiceException"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="AppModuleSearchServiceSoapHttp" type="tns:AppModuleSearchService">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="search">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/search"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
        <wsdl:operation name="saveSearch">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/saveSearch"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
        <wsdl:operation name="getSavedSearches">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/getSavedSearches"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
        <wsdl:operation name="getSavedSearchDetails">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/getSavedSearchDetails"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
        <wsdl:operation name="getSavedSearch">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/getSavedSearch"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
        <wsdl:operation name="getEngineInstances">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/getEngineInstances"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
        <wsdl:operation name="getDisplayName">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/getDisplayName"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
        <wsdl:operation name="deleteSearch">
            <soap:operation soapAction="/oracle/ecsf/service/query/common/deleteSearch"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
            <wsdl:fault name="ServiceException">
                <soap:fault name="ServiceException" use="literal" encodingStyle=""/>
            </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="AppModuleSearchService">
        <wsdl:port name="AppModuleSearchServiceSoapHttpPort" binding="tns:AppModuleSearchServiceSoapHttp">
            <soap:address location="http://localhost:7101/Application1-ViewController-context-root/AppModuleSearchService"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Example 31-24 ECSF Web Service XSD

<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="/oracle/ecsf/service/query/common/types/"
     xmlns:tns="/oracle/ecsf/service/query/common/types/" xmlns:ns0="http://xmlns.example.com/adf/svc/errors/">
    <import namespace="http://xmlns.example.com/adf/svc/errors/" schemaLocation="classpath:/META-INF/wsdl/ServiceException.xsd"/>
    <element name="search">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="queryMetadataRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="searchResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="saveSearch">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="savedSearchRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="saveSearchResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getSavedSearches">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="savedSearchRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getSavedSearchesResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getSavedSearchDetails">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="savedSearchRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getSavedSearchDetailsResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getSavedSearch">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="savedSearchRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getSavedSearchResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getEngineInstances">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="engineInstanceRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getEngineInstancesResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getDisplayName">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="displayNameRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="getDisplayNameResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="deleteSearch">
        <complexType>
            <sequence>
                <element name="userName" type="string"/>
                <element name="savedSearchRequest" type="string"/>
            </sequence>
        </complexType>
    </element>
    <element name="deleteSearchResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
</schema>

31.8.3 Web Service Request XSDs and XMLs

Each of the web service methods takes in a username and a request XML. The username is used to bind to the SearchContext. The request XML is passed to the SearchService to perform query related functions.

The request XMLs for the web service methods are based on the following request XSDs:

31.8.3.1 SavedSearch Request XSD

The request XMLs for the getSavedSearch(), getSavedSearches(), saveSearch(), deleteSearch(), and getSavedSearchDetails() methods are based on the SavedSearch request XSD, shown in Example 31-25.

Example 31-25 SavedSearch Request XSD

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:element name="request">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="savedSearch"/>
        <xsd:element name="name" type="xsd:string"/>
        <xsd:element name="callerctx" type="xsd:string"/>
        <xsd:element name="locale" type="xsd:string"/>
        <xsd:element name="scope" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="savedSearch">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="description" type="xsd:string"/>
        <xsd:element name="callerctx" type="xsd:string"/>
        <xsd:element name="query" type="xsd:string"/>
        <xsd:element name="ispublic" type="xsd:boolean"/>
        <xsd:element name="userid" type="xsd:string"/>
        <xsd:element name="detailsid" type="xsd:integer" minOccurs="0"/>
        <xsd:element ref="queryMetaData"/>
      </xsd:sequence>
      <xsd:attribute name="name" type="xsd:string" use="required"/>
      <xsd:attribute name="id" type="xsd:integer" use="required"/>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="queryMetaData">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="query" type="xsd:string"/>
        <xsd:element name="page" type="xsd:integer"/>
        <xsd:element name="lang" type="xsd:string"/>
        <xsd:element name="pageSize" type="xsd:integer"/>
        <xsd:element name="searchCtrl" type="xsd:string" minOccurs="0"/>
        <xsd:element name="soname" type="xsd:string" minOccurs="0"/>
        <xsd:element name="facetPaths" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="facetPath" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" maxOccurs="unbounded"
                                 minOccurs="0">
                      <xsd:complexType>
                        <xsd:attribute name="name" type="xsd:string"
                                       use="required"/>
                        <xsd:attribute name="value" type="xsd:string"
                                       use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="categories" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="category" maxOccurs="unbounded" minOccurs="0">
                <xsd:complexType>
                  <xsd:attribute name="name" type="xsd:string" use="required"/>
                  <xsd:attribute name="eid" type="xsd:integer" use="required"/>
                  <xsd:attribute name="compid" type="xsd:integer"
                                 use="required"/>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="tags" type="xsd:string" minOccurs="0"/>
        <xsd:element name="filters" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="filter" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:simpleContent>
                    <xsd:extension base="xsd:string">
                      <xsd:attribute name="name" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="operator" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="type" type="xsd:string"
                                     use="required"/>
                    </xsd:extension>
                  </xsd:simpleContent>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

The SavedSearch request XSD describes the set of rules that the request XMLs must follow in order to be valid. Examples of valid request XMLs include the following:

  • getSavedSearch()

    <request>
      <name>ECSF_JUNIT_SVSEARCH</name>
      <callerctx>null</callerctx>
      <locale>en_us</locale>
      <scope>LOCAL</scope> /*use GLOBAL for global scope*/
    </request>
    
  • getSavedSearches()

    <request>
      <callerctx>%</callerctx>
      <locale>en_us</locale>
      <scope>LOCAL</scope> /*use GLOBAL for global scope*/
    </request>
    
  • saveSearch()

    <request>
      <savedSearch name="ECSF_JUNIT_SVSEARCH" id="1" compid="2">
        <description>
          <![CDATA[Updated Description]]>
        </description>
        <callerctx>
          <![CDATA[]]>
        </callerctx>
        <query>
          <![CDATA[]]>
        </query>
        <ispublic>
          <![CDATA[false]]>
        </ispublic>
        <userid>
          <![CDATA[junit]]>
        </userid>
        <detailsid>
          <![CDATA[1]]>
        </detailsid>
        <queryMetaData>
          <query>
            <![CDATA[%]]>
          </query>
          <page>1</page>
          <lang>en</lang>
          <pageSize>10</pageSize>
          <categories>
            <category name="runtime.EmpView" eid="1" compid="0"></category>
          </categories>
        </queryMetaData>
      </savedSearch>
      <name></name>
      <callerctx>null</callerctx>
      <locale>en_us</locale>
      <scope>LOCAL</scope> /*use GLOBAL for global scope*/
    </request>
    
  • deleteSearch()

    <request>
      <savedSearch name="DeleteSavedSearch" id="100010033142848" compid="2">
        <description>
          <![CDATA[Junit Saved Search]]>
        </description>
        <callerctx>
          <![CDATA[CRM]]>
        </callerctx>
        <query>
          <![CDATA[%]]>
        </query>
        <ispublic>
          <![CDATA[false]]>
        </ispublic>
        <userid>
          <![CDATA[junit]]>
        </userid>
        <detailsid>
          <![CDATA[100010033142849]]>
        </detailsid>
        <queryMetaData>
          <query>
            <![CDATA[%]]>
          </query>
          <page>1</page>
          <lang>en</lang>
          <pageSize>10</pageSize>
          <categories>
            <category name="runtime.EmpView" eid="1" compid="3"></category>
          </categories>
        </queryMetaData>
      </savedSearch>
      <name></name>
      <callerctx>null</callerctx>
      <locale>en_us</locale>
      <scope>LOCAL</scope> /*use GLOBAL for global scope*/
    </request>
    
  • getSavedSearchDetails()

    <request>
      <savedSearch name="SavedSearch" id="100010033142848" compid="2">
        <description>
          <![CDATA[Junit Federation Saved Search]]>
        </description>
        <callerctx>
          <![CDATA[CRM]]>
        </callerctx>
        <query>
          <![CDATA[%]]>
        </query>
        <ispublic>
          <![CDATA[false]]>
        </ispublic>
        <userid>
          <![CDATA[junit]]>
        </userid>
        <detailsid>
          <![CDATA[100010033142849]]>
        </detailsid>
      </savedSearch>
      <callerctx>null</callerctx>
      <locale>en_us</locale>
      <scope>LOCAL</scope> /*use GLOBAL for global scope*/
    </request>
    

31.8.3.2 QueryMetaData Request XSD

The request XML for the search(String userName, String queryMetaDataRequest) method is based on the QueryMetaData request XSD, shown in Example 31-26.

Example 31-26 QueryMetaData Request XSD

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:element name="request">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="queryMetaData"/>
        <xsd:element name="locale" type="xsd:string"/>
        <xsd:element name="scope" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="queryMetaData">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="query" type="xsd:string"/>
        <xsd:element name="page" type="xsd:integer"/>
        <xsd:element name="lang" type="xsd:string"/>
        <xsd:element name="pageSize" type="xsd:integer"/>
        <xsd:element name="searchCtrl" type="xsd:string" minOccurs="0"/>
        <xsd:element name="soname" type="xsd:string" minOccurs="0"/>
        <xsd:element name="facetPaths" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="facetPath" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" maxOccurs="unbounded"
                                 minOccurs="0">
                      <xsd:complexType>
                        <xsd:attribute name="name" type="xsd:string"
                                       use="required"/>
                        <xsd:attribute name="value" type="xsd:string"
                                       use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="categories" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="category" maxOccurs="unbounded" minOccurs="0">
                <xsd:complexType>
                  <xsd:attribute name="name" type="xsd:string" use="required"/>
                  <xsd:attribute name="eid" type="xsd:integer" use="required"/>
                  <xsd:attribute name="compid" type="xsd:integer"
                                 use="required"/>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="tags" type="xsd:string" minOccurs="0"/>
        <xsd:element name="filters" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="filter" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:simpleContent>
                    <xsd:extension base="xsd:string">
                      <xsd:attribute name="name" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="operator" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="type" type="xsd:string"
                                     use="required"/>
                    </xsd:extension>
                  </xsd:simpleContent>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

The QueryMetaData request XSD describes the set of rules that the request XMLs must follow in order to be valid. An example of a valid request XML for search() is:

<request>
  <queryMetaData>
    <query>
      <![CDATA[*]]>
    </query>
    <page>1</page>
    <lang>en</lang>
    <pageSize>10</pageSize>
    <categories>
      <category name="runtime.EmpView" eid="1" compid="2"></category>
    </categories>
  </queryMetaData>
  <locale>en_us</locale>
  <scope>LOCAL</scope> /*use GLOBAL for global scope*/
</request>

31.8.3.3 engineInstanceRequest Request XSD

The request XML for the getEngineInstances(String userName, String engineInstanceRequest) method is based on the engineInstanceRequest request XSD, shown in Example 31-27.

Example 31-27 engineInstanceRequest Request XSD

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:element name="request">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="enginetypeid"/>
        <xsd:element name="locale" type="xsd:string"/>
        <xsd:element name="scope" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

The engineInstanceRequest request XSD describes the set of rules that the request XMLs must follow in order to be valid. An example of a valid request XML for getEngineInstances() is:

<request>
  <enginetypeid>-1</enginetypeid>
  <locale>en_us</locale>
  <scope>LOCAL</scope> /*use GLOBAL for global scope*/
</request>

31.8.4 Web Service Response XSDs

For each of the web service calls, if the call is successful an XML string is returned. If the web service call results in an error, a service error XML string is returned. The client can then call the ServiceUtil() class to deserialize the XML string into ECSF runtime metadata objects or deserialize the service error XML string into an exception message.

When the query web service requests are successful, a response message is returned. The XML strings for successful web service calls are based on the XSDs for the following methods:

If any of the query web service requests result in an exception, the exception is wrapped in a service error XML response message.The service error XML strings are based on the following XSD:

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:element name="serviceError">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="code" type="xsd:string" minOccurs="0"/>
        <xsd:element name="message" type="xsd:string"/>
        <xsd:element name="stack" type="xsd:string"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

31.8.4.1 getSavedSearch()

The web service response message for the method String getSavedSearch(String userName, String savedSearchRequest) is based on the following XSD:

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:element name="savedSearch">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="description" type="xsd:string"/>
        <xsd:element name="callerctx" type="xsd:string"/>
        <xsd:element name="query" type="xsd:string"/>
        <xsd:element name="ispublic" type="xsd:boolean"/>
        <xsd:element name="userid" type="xsd:string"/>
        <xsd:element name="detailsid" type="xsd:integer" minOccurs="0"/>
        <xsd:element ref="queryMetaData"/>
      </xsd:sequence>
      <xsd:attribute name="name" type="xsd:string" use="required"/>
      <xsd:attribute name="id" type="xsd:integer" use="required"/>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="queryMetaData">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="query" type="xsd:string"/>
        <xsd:element name="page" type="xsd:integer"/>
        <xsd:element name="lang" type="xsd:string"/>
        <xsd:element name="pageSize" type="xsd:integer"/>
        <xsd:element name="searchCtrl" type="xsd:string" minOccurs="0"/>
        <xsd:element name="soname" type="xsd:string" minOccurs="0"/>
        <xsd:element name="facetPaths" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="facetPath" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" maxOccurs="unbounded"
                                 minOccurs="0">
                      <xsd:complexType>
                        <xsd:attribute name="name" type="xsd:string"
                                       use="required"/>
                        <xsd:attribute name="value" type="xsd:string"
                                       use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="categories" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="category" maxOccurs="unbounded" minOccurs="0">
                <xsd:complexType>
                  <xsd:attribute name="name" type="xsd:string" use="required"/>
                  <xsd:attribute name="eid" type="xsd:integer" use="required"/>
                  <xsd:attribute name="compid" type="xsd:integer"
                                 use="required"/>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="tags" type="xsd:string" minOccurs="0"/>
        <xsd:element name="filters" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="filter" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:simpleContent>
                    <xsd:extension base="xsd:string">
                      <xsd:attribute name="name" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="operator" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="type" type="xsd:string"
                                     use="required"/>
                    </xsd:extension>
                  </xsd:simpleContent>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

31.8.4.2 getSavedSearches()

The web service response message for the method String getSavedSearches(String userName, String savedSearchRequest) is based on the following XSD:

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:include schemaLocation="saved-search.xsd"/>
  <xsd:element name="savedSearches">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="savedSearch" maxOccurs="unbounded" minOccurs="0"/>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

31.8.4.3 saveSearch()

The web service response message for the method String saveSearch(String userName, String savedSearchRequest) is the same as the response for getSavedSearch(). For information, see Section 31.8.4.1, "getSavedSearch()".

31.8.4.4 deleteSearch()

The web service response message for the method String deleteSearch(String userName, String savedSearchRequest) is the string success.

31.8.4.5 getSavedSearchDetails

The web service response message for the method String getSavedSearchDetails(String userName, String savedSearchRequest) is based on the following XSD:

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:element name="savedSearchDetails">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="queryMetaData"/>
      </xsd:sequence>
      <xsd:attribute name="savedSearchId" type="xsd:integer" use="required"/>
      <xsd:attribute name="id" type="xsd:integer" use="required"/>
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="queryMetaData">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="query" type="xsd:string"/>
        <xsd:element name="page" type="xsd:integer"/>
        <xsd:element name="lang" type="xsd:string"/>
        <xsd:element name="pageSize" type="xsd:integer"/>
        <xsd:element name="searchCtrl" type="xsd:string" minOccurs="0"/>
        <xsd:element name="soname" type="xsd:string" minOccurs="0"/>
        <xsd:element name="facetPaths" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="facetPath" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="value" maxOccurs="unbounded"
                                 minOccurs="0">
                      <xsd:complexType>
                        <xsd:attribute name="name" type="xsd:string"
                                       use="required"/>
                        <xsd:attribute name="value" type="xsd:string"
                                       use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="categories" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="category" maxOccurs="unbounded" minOccurs="0">
                <xsd:complexType>
                  <xsd:attribute name="name" type="xsd:string" use="required"/>
                  <xsd:attribute name="eid" type="xsd:integer" use="required"/>
                  <xsd:attribute name="compid" type="xsd:integer"
                                 use="required"/>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="tags" type="xsd:string" minOccurs="0"/>
        <xsd:element name="filters" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="filter" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:simpleContent>
                    <xsd:extension base="xsd:string">
                      <xsd:attribute name="name" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="operator" type="xsd:string"
                                     use="required"/>
                      <xsd:attribute name="type" type="xsd:string"
                                     use="required"/>
                    </xsd:extension>
                  </xsd:simpleContent>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

31.8.4.6 search()

The web service response message for the method String search(String userName, String queryMetaDataRequest) is based on the following XSD:

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:include schemaLocation="query-metadata.xsd"/>            
  <xsd:element name="searchResults">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="hitsMetaData" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="query" type="xsd:string"/>
              <xsd:element name="page" type="xsd:integer"/>
              <xsd:element name="pages" type="xsd:integer"/>
              <xsd:element name="hits" type="xsd:integer"/>
              <xsd:element ref="queryMetaData"/>
              <xsd:element name="categories" minOccurs="0">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="category" maxOccurs="unbounded">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element name="searchableObjects" minOccurs="0">
                            <xsd:complexType>
                              <xsd:sequence>
                                <xsd:element name="searchableObject" maxOccurs="unbounded">
                                  <xsd:complexType>
                                    <xsd:attribute name="name" type="xsd:string" use="required"/>
                                    <xsd:attribute name="id" type="xsd:integer" use="required"/>
                                    <xsd:attribute name="lastTimeCrawled" type="xsd:integer" use="optional"/>
                                    <xsd:attribute name="displayName" type="xsd:string" use="required"/>
                                  </xsd:complexType>
                                </xsd:element>
                              </xsd:sequence>
                            </xsd:complexType>
                          </xsd:element>
                        </xsd:sequence>
                        <xsd:attribute name="name" type="xsd:string" use="required"/>
                        <xsd:attribute name="id" type="xsd:integer" use="required"/>
                        <xsd:attribute name="eid" type="xsd:integer" use="required"/>
                        <xsd:attribute name="external" type="xsd:string" use="required"/>
                        <xsd:attribute name="displayName" type="xsd:string" use="required"/>
                        <xsd:attribute name="scope" type="xsd:string" use="required"/>
                        <xsd:attribute name="applid" type="xsd:string" use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="altwords" type="xsd:string" minOccurs="0"/>
              <xsd:element name="timespent" type="xsd:integer"/>
              <xsd:element name="facets" minOccurs="0">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="facet" maxOccurs="unbounded" minOccurs="0">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element name="pathEntry" maxOccurs="unbounded" minOccurs="0">
                            <xsd:complexType>
                              <xsd:attribute name="pname" type="xsd:string" use="required"/>
                              <xsd:attribute name="pdispname" type="xsd:string" use="required"/>
                              <xsd:attribute name="pisleaf" type="xsd:boolean" use="required"/>
                              <xsd:attribute name="value" type="xsd:string" use="required"/>
                              <xsd:attribute name="count" type="xsd:integer" use="required"/>
                              <xsd:attribute name="displayValue" type="xsd:string" use="required"/>
                            </xsd:complexType>
                          </xsd:element>
                          <xsd:element name="entries" minOccurs="0">
                            <xsd:complexType>
                              <xsd:sequence>
                                <xsd:element name="entry" maxOccurs="unbounded">
                                  <xsd:complexType>
                                    <xsd:attribute name="value" type="xsd:string" use="required"/>
                                    <xsd:attribute name="displayValue" type="xsd:string" use="required"/>
                                  </xsd:complexType>
                                </xsd:element>
                              </xsd:sequence>
                            </xsd:complexType>
                          </xsd:element>
                        </xsd:sequence>
                        <xsd:attribute name="root" type="xsd:string" use="required"/>
                        <xsd:attribute name="name" type="xsd:string" use="required"/>
                        <xsd:attribute name="dispname" type="xsd:string" use="required"/>
                        <xsd:attribute name="isleaf" type="xsd:boolean" use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
              <xsd:element name="filters" minOccurs="0">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="filter" maxOccurs="unbounded">
                      <xsd:complexType>
                        <xsd:simpleContent>
                          <xsd:extension base="xsd:string">
                            <xsd:attribute name="name" type="xsd:string" use="required"/>
                            <xsd:attribute name="operator" type="xsd:string" use="required"/>
                            <xsd:attribute name="type" type="xsd:string" use="required"/>
                          </xsd:extension>
                        </xsd:simpleContent>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="results" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="result" maxOccurs="unbounded">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="title" type="xsd:string"/>
                    <xsd:element name="displayUrl" type="xsd:string"/>
                    <xsd:element name="score" type="xsd:integer"/>
                    <xsd:element name="searchableObject" minOccurs="0">
                      <xsd:complexType>
                        <xsd:attribute name="name" type="xsd:string" use="required"/>
                        <xsd:attribute name="eid" type="xsd:integer" use="required"/>
                      </xsd:complexType>
                    </xsd:element>
                    <xsd:element name="content" type="xsd:string"/>
                    <xsd:element name="keywords" type="xsd:string" minOccurs="0"/>
                    <xsd:element name="lang" type="xsd:string"/>
                    <xsd:element name="attributes">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element name="attribute" maxOccurs="unbounded" minOccurs="0">
                            <xsd:complexType>
                              <xsd:simpleContent>
                                <xsd:extension base="xsd:string">
                                  <xsd:attribute name="name" type="xsd:string" use="required"/>
                                  <xsd:attribute name="binding" type="xsd:string" use="required"/>
                                  <xsd:attribute name="type" type="xsd:string" use="required"/>
                                  <xsd:attribute name="displayName" type="xsd:string" use="required"/>
                                </xsd:extension>
                              </xsd:simpleContent>
                            </xsd:complexType>
                          </xsd:element>
                        </xsd:sequence>
                      </xsd:complexType>
                    </xsd:element>
                    <xsd:element name="tags" type="xsd:string" minOccurs="0"/>
                  </xsd:sequence>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

31.8.4.7 getEngineInstances()

The web service response message for the method String getEngineInstances(String userName, String engineInstanceRequest) is based on the following XSD:

<?xml version="1.0" encoding="US-ASCII" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns="http://www.example.com"
            targetNamespace="http://www.example.com"
            elementFormDefault="qualified">
  <xsd:element name="engineInstances">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="engineInstance" maxOccurs="unbounded" minOccurs="0">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="category" maxOccurs="unbounded" minOccurs="0">
                <xsd:complexType>
                  <xsd:sequence>
                    <xsd:element name="searchableObjects" minOccurs="0">
                      <xsd:complexType>
                        <xsd:sequence>
                          <xsd:element name="searchableObject" maxOccurs="unbounded" minOccurs="0">
                            <xsd:complexType>
                              <xsd:sequence>
                                <xsd:element name="fieldDefs" minOccurs="0">
                                  <xsd:complexType>
                                    <xsd:sequence>
                                      <xsd:element name="fieldDef" maxOccurs="unbounded" minOccurs="0">
                                        <xsd:complexType>
                                          <xsd:attribute name="name" type="xsd:string" use="required"/>
                                          <xsd:attribute name="binding" type="xsd:string" use="required"/>
                                          <xsd:attribute name="dataType" type="xsd:string" use="required"/>
                                          <xsd:attribute name="isStored" type="xsd:boolean" use="required"/>
                                          <xsd:attribute name="isSecure" type="xsd:boolean" use="required"/>
                                        </xsd:complexType>
                                      </xsd:element>
                                    </xsd:sequence>
                                  </xsd:complexType>
                                </xsd:element>
                                <xsd:element name="facetDefs" minOccurs="0">
                                  <xsd:complexType>
                                    <xsd:sequence>
                                      <xsd:element name="facetDef" maxOccurs="unbounded">
                                        <xsd:complexType>
                                          <xsd:sequence>
                                            <xsd:element name="facetEntryDef" maxOccurs="unbounded" minOccurs="0">
                                              <xsd:complexType>
                                                <xsd:attribute name="value" type="xsd:string" use="required"/>
                                                <xsd:attribute name="displayValue" type="xsd:string" use="required"/>
                                              </xsd:complexType>
                                            </xsd:element>
                                          </xsd:sequence>
                                          <xsd:attribute name="name" type="xsd:string" use="required"/>
                                          <xsd:attribute name="binding" type="xsd:string" use="required"/>
                                          <xsd:attribute name="displayName" type="xsd:string" use="required"/>
                                          <xsd:attribute name="isleaf" type="xsd:boolean" use="required"/>
                                        </xsd:complexType>
                                      </xsd:element>
                                    </xsd:sequence>
                                  </xsd:complexType>
                                </xsd:element>
                                <xsd:element name="actionDefs" minOccurs="0">
                                  <xsd:complexType>
                                    <xsd:sequence>
                                      <xsd:element name="actionDef" maxOccurs="unbounded" minOccurs="0">
                                        <xsd:complexType>
                                          <xsd:sequence>
                                            <xsd:element name="target" type="xsd:string"/>
                                            <xsd:element name="title" type="xsd:string"/>
                                            <xsd:element name="params" minOccurs="0">
                                              <xsd:complexType>
                                                <xsd:sequence>
                                                  <xsd:element name="param" maxOccurs="unbounded" minOccurs="0">
                                                    <xsd:complexType>
                                                      <xsd:simpleContent>
                                                        <xsd:extension base="xsd:string">
                                                          <xsd:attribute name="name" type="xsd:string" use="required"/>
                                                        </xsd:extension>
                                                      </xsd:simpleContent>
                                                    </xsd:complexType>
                                                  </xsd:element>
                                                </xsd:sequence>
                                              </xsd:complexType>
                                            </xsd:element>
                                          </xsd:sequence>
                                          <xsd:attribute name="name" type="xsd:string" use="required"/>
                                          <xsd:attribute name="isDefault" type="xsd:string" use="required"/>
                                          <xsd:attribute name="type" type="xsd:string" use="required"/>
                                        </xsd:complexType>
                                      </xsd:element>
                                    </xsd:sequence>
                                  </xsd:complexType>
                                </xsd:element>
                              </xsd:sequence>
                              <xsd:attribute name="name" type="xsd:string" use="required"/>
                              <xsd:attribute name="id" type="xsd:integer" use="required"/>
                              <xsd:attribute name="displayName" type="xsd:string" use="required"/>
                            </xsd:complexType>
                          </xsd:element>
                        </xsd:sequence>
                      </xsd:complexType>
                    </xsd:element>
                  </xsd:sequence>
                  <xsd:attribute name="name" type="xsd:string" use="required"/>
                  <xsd:attribute name="eid" type="xsd:integer" use="required"/>
                  <xsd:attribute name="id" type="xsd:integer" use="required"/>
                  <xsd:attribute name="external" type="xsd:boolean" use="required"/>
                  <xsd:attribute name="displayName" type="xsd:string"/>
                  <xsd:attribute name="scope" type="xsd:string" use="required"/>
                  <xsd:attribute name="applid" type="xsd:string" use="required"/>
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
            <xsd:attribute name="name" type="xsd:string" use="required"/>
            <xsd:attribute name="id" type="xsd:integer" use="required"/>
            <xsd:attribute name="engineType" type="xsd:string" use="required"/>
            <xsd:attribute name="engineTypeId" type="xsd:integer" use="required"/>
            <xsd:attribute name="displayName" type="xsd:string" use="required"/>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

31.8.5 How to Invoke the ECSF Web Service

Invoke the ECSF web service by creating a JAX-WS proxy using the Oracle JDeveloper Web Service Proxy wizard. Then, modify the client Java class named AppModuleSearchServiceSoapHttpPortClient to add calls to the ECSF web service.

Note:

It is assumed that the web service is deployed and running on a remote Oracle WebLogic Server instance.

31.8.5.1 Creating a JAX-WS Web Service Proxy

Creating a JAX-WS web service proxy using the Oracle JDeveloper Create Web Service Proxy wizard generates all classes and Java files for the web service enabled methods under the package oracle.ecsf.service.query.common.

To create a JAX-WS web service proxy:

  1. In the Application Navigator, right-click the project in which you want to create the web service proxy, and choose New.

  2. In the New Gallery, expand Business Tier, select Web Services and then Web Service Proxy, and click OK.

  3. On the Select Web Service Description page of the wizard, enter the URL for the WSDL that was generated when the application was deployed on the Oracle WebLogic Server, for example, http://myhost.us.example.com:7101/CrmSearchService/AppModuleSearchService?WSDL, then tab out of the field.

    If the Next button is not enable, click Why Not? to understand what problem JDeveloper encountered when trying to read the WSDL document. If necessary, fix the problem after verifying the URL and repeat this step.

    When the wizard displays an enabled Next button, then Oracle JDeveloper has recognized and validated the WSDL document.

  4. For Specify Default Mapping Options, enter or choose a Java package name for the generated web service proxy class.

  5. Click Next.

  6. Continue through the pages of the wizard to specify details about the web service proxy. For more information about each page of the wizard, press F1 or click Help.

  7. Click Finish.

  8. Copy the following files from the system11.xxx/DefaultDomain/config/fmwconfig Oracle WebLogic Server directory to the JAX-WS Client application's META-INF directory.

    • jps-config.xml

    • cwallet.sso

    • default-keystore.jks

31.8.5.2 Modifying the AppModuleSearchServiceSoapHttpPortClient Class

After you create the JAX-WS proxy, modify the AppModuleSearchServiceSoapHttpPortClient class to include the calls to web service methods. Example 31-28 shows an example of a modified class.

Example 31-28 Sample Client Class

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import java.net.URL;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;

import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import oracle.ecsf.SearchException;
import oracle.ecsf.SearchHits;
import oracle.ecsf.client.SearchGroup;
import oracle.ecsf.client.dataobject.SavedSearch;
import oracle.ecsf.client.dataobject.SavedSearchDetails;
import oracle.ecsf.impl.QueryMetaDataImpl;
import oracle.ecsf.meta.MetaEngineInstance;

import oracle.ecsf.service.query.common.AppModuleSearchService;
import oracle.ecsf.service.query.common.AppModuleSearchService_Service;
import oracle.ecsf.service.query.ws.marshaller.ServiceUtil;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class AppModuleSearchServiceSoapHttpPortClient
{

   private AppModuleSearchService_Service appModuleSearchServiceService;
   AppModuleSearchService appModuleSearchService;

   String wlsHost = null;
   String wlsPort = null;
   String wlsPassword = null;
   final Long engineInstanceId = 1L;

   public AppModuleSearchServiceSoapHttpPortClient()
   {
      initialize();
   }

   public void initialize() throws Exception
   {
      if (System.getProperty("oracle.ecsf.debug.jdev") == null)
      {
         System.setProperty("oracle.security.jps.config", findFile("META-INF/jps-config.xml"));
      }
      try
      {
         //calling method initializeWLSparams to read parameter from file ecsf.properties
         initializeWLSParams();

         URL aURL = new URL("http://" + wlsHost + ":" + wlsPort + "/CrmSearchService/AppModuleSearchService?WSDL");
         QName qName = new QName("/oracle/ecsf/service/query/common/", "AppModuleSearchService");

         appModuleSearchServiceService = new AppModuleSearchService_Service(aURL, qName);
         appModuleSearchService = appModuleSearchServiceService.getAppModuleSearchServiceSoapHttpPort();

          ((BindingProvider)appModuleSearchService).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "weblogic");
         ((BindingProvider)appModuleSearchService).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, wlsPassword);
      }
      catch (Exception e)
      {
         e.printStackTrace();
         System.out.println("Excpetion " + e);
      }
   }

   /**
    * invoke the getEngineInstances() method exposed in WebService.
    */
   public void testGetEngineInstance()
   {
      ServiceUtil sUtil = new ServiceUtil();
      try
      {
         String query = "<request><enginetypeid>2</enginetypeid><locale>en-US</locale><scope>LOCAL</scope></request>";
         String response = appModuleSearchService.getEngineInstances("weblogic", query);
         Collection<MetaEngineInstance> instances = sUtil.toEngineInstances(response);
      }
      catch (Exception e)
      {
      }
   }

   /**
    * invoke the search() method exposed in WebService.
    */
   public void testSearch()
   {
      ServiceUtil sUtil = new ServiceUtil();
      String request = "<request><queryMetaData><query><![CDATA[*]]></query><page>1</page><lang>en</lang><pageSize>10</pageSize><categories><category name=\"EmpSearch\" eid=\"100010024205617\" compid=\"100010026129681\"></category></categories></queryMetaData><request>";
      try
      {
         String response = appModuleSearchService.search("weblogic", request);
         SearchHits hits = sUtil.toSearchHits(response);
      }
      catch (Exception e)
      {
      }
   }

   /**
    * invoke the getSavedSearch() method exposed in WebService.
    */
   public void testGetSavedSearch()
   {
      ServiceUtil sUtil = new ServiceUtil();
      try
      {
         String request = "<request><name>TestServiceproxy</name><callerctx>GLOBAL</callerctx><locale>en-US</locale><scope></scope></request>";
         String response = appModuleSearchService.getSavedSearch("weblogic", request);
         SavedSearch ss = sUtil.toSavedSearch(response);
      }
      catch (Exception e)
      {
      }
   }

   /**
    * invoke the getSavedSearches() method exposed in WebService.
    */
   public void testgetSavedSearches()
   {
      ServiceUtil sUtil = new ServiceUtil();
      try
      {
         String request = "<request><name></name><callerctx>LOCAL</callerctx><locale>en-US</locale><scope>LOCAL</scope></request>";
         String response = appModuleSearchService.getSavedSearches("weblogic", request);
         ArrayList<SavedSearch> savedSearches = sUtil.toSavedSearches(response);
      }
      catch (Exception e)
      {
      }
   }

   /**
    * invoke the savedSearch() method exposed in WebService.
    */
   public void testSaveSearch()
   {
      ServiceUtil sUtil = new ServiceUtil();
      try
      {
         SavedSearch savedSearch = createSavedSearch("TestServiceproxy");
         String request = sUtil.toString(savedSearch, true);
         appModuleSearchService.saveSearch("weblogic", request);
      }
      catch (Exception e)
      {
      }
   }


   /**
    * invoke the getSavedSearchDetails() method exposed in WebService.
    */
   public void testGetSavedSearchDetails()
   {
      ServiceUtil sUtil = new ServiceUtil();
      try
      {
         String ssRequest = "<request><name>TestServiceproxy</name><callerctx>GLOBAL</callerctx><locale>en-US</locale><scope></scope></request>";
         String ssResponse = appModuleSearchService.getSavedSearch("weblogic", ssRequest);

         String request = "<request>" + ssResponse + "<callerctx>null</callerctx><locale>en_us</locale><scope>LOCAL</scope></request>";
         String response = appModuleSearchService.getSavedSearchDetails("weblogic", request);

         SavedSearchDetails ssDetails = sUtil.toSavedSearchDetails(response);
      }
      catch (Exception e)
      {
      }
   }

   /**
    * invoke the deleteSearch() method exposed in WebService.
    */
   public void testDeleteSavedSearch()
   {
      try
      {
         String ssRequest = "<request>QA<name>TestServiceproxy</name><callerctx>GLOBAL</callerctx><locale>en-US</locale><scope></scope></request>";
         String ssResponse = appModuleSearchService.getSavedSearch("weblogic", ssRequest);

         String request = "<request>" + ssResponse + "<callerctx>null</callerctx><locale>en_us</locale><scope>LOCAL</scope></request>";
         String response = appModuleSearchService.deleteSearch("weblogic", request);
      }
      catch (Exception e)
      {
      }
   }

   private SavedSearch createSavedSearch(String sSearchName) throws SearchException
   {
      QueryMetaDataImpl queryMetaData = new QueryMetaDataImpl();
      queryMetaData.setQueryString("%");
      queryMetaData.setPageSize(10);
      queryMetaData.setCurrentPage(1);
      SearchGroup[] sgs = new SearchGroup[] { new SearchGroup("EmpSearch", "EmpSearch", engineInstanceId, null, null) };
      queryMetaData.setSearchGroups(sgs);

      SavedSearch savedSearch =
         new SavedSearch(SavedSearch.VAL_INVALID_ID, "weblogic", null, null, null, sSearchName, "search created for WS interop", null, queryMetaData.getQueryString(), false, "GLOBAL");

      SavedSearchDetails ssd = new SavedSearchDetails(1L, "weblogic", null, null, null, SavedSearch.VAL_INVALID_ID, queryMetaData, null);
      savedSearch.setSavedSearchDetails(ssd);

      return savedSearch;
   }
  
   private void initializeWLSParams()
   {
      //load wls host/port and ses wls host/port from ecsf.properties
      Properties prop = new Properties();
      FileInputStream fis;

      try
      {
         fis = new FileInputStream(System.getenv("T_WORK") + File.separator + "ecsf.properties");
         prop.load(fis);
         fis.close();

         wlsHost = prop.getProperty("WLS_HOST");
         wlsPort = prop.getProperty("WLS_PORT");
         wlsPassword = prop.getProperty("WLS_PWD");
      }
      catch (FileNotFoundException e)
      {
         System.out.println("File not found exeception : " + e.getMessage());
      }
      catch (IOException e)
      {
         System.out.println("IO exception : " + e.getMessage());
      }
   }

   private String findFile(String findme)
   {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      URL u = loader.getResource(findme);
      String fullname = u.getFile();
      return fullname;
   }
}

The sample client class illustrates how each of the methods are called and how the XMLs are deserialized.

31.9 Localizing ECSF Artifacts

When developing applications for international users, it is necessary to customize the display of data by adding locale-specific components and translating text.

Locales identify a specific language and geographic region. You must customize how ECSF presents and formats messages (that is, the data displayed to users) based on their locale. Locale affects:

  • Messages that you create during design time (for example, facet display names) and are displayed to the users who perform the queries.

  • Messages you create during deployment (for example, searchable object names, search category names, and index schedule names displayed during crawl management).

  • Crawlable content.

ECSF must determine the locale in order to display the messages in the appropriate language for the user.

31.9.1 How to Translate Strings in Groovy Expressions

ECSF provides a way for you to use translated strings to define the Title, Body, and Keywords properties of searchable objects.

The ECSFFormat class provides two functions that can be called in Groovy expressions:

  • format(String key, Object param1, Object param 2,...) takes in a resource bundle key name, as well as up to ten parameters, to be added to the formatted string. This function allows you to use translatable strings in the Groovy expression for the Title, Body, and Keywords properties.

  • getLabel(String attributeName) takes in an attribute name and gets the translated label for this attribute. This function provides a way for you to get the translated value of an attribute label.

ECSF also reserves a Groovy entity formatter that provides two functions:

  • formatter.format(String key, Object param1, Object param 2,...) allows you to format a string based on a template.

  • formatter.getLabel(String attributeName) can be used to retrieve the UI label defined for an attribute.

To use translated strings in Groovy expressions, you must:

  1. Associate a resource bundle to the view object.

  2. Use the format() function in the Groovy expressions for the Title, Body, and Keywords properties.

  3. Associate a translated label to the attribute.

  4. Use the getLabel() function in the Groovy expressions for the Title, Body, and Keywords properties.

31.9.1.1 Associating Resource Bundles to View Objects

In order to define translatable strings for use as part of the crawlable search properties, you must associate a resource bundle to the view object and define the translatable strings on the resource bundle.

To add resource bundles to view objects:

  1. In the overview editor, select the General navigation tab.

  2. Click the Add icon in the Custom Properties table header and select Translatable Property to open the Select Text Resource dialog.

  3. Select a resource bundle from the Resource Bundle dropdown menu.

    Note:

    If there is no resource bundle associated with the view object, then an external resource bundle is created in the application. Save the new resource bundle externally.
  4. In the Display Value field, enter a string to associate with the key in the page's resource bundle. For example, enter

    Identification Number: {0}. {1} {2} {3} {4} {5} {6}.

  5. In the Key field, enter a string to uniquely identify a locale-specific object in the resource bundle. For example, enter EMPVIEW_BODY.

  6. In the Description field, enter a description of any length for the key and value pair. For example, enter body value for crawling.

  7. Click Save and Select.

    A new entry is added to the resource bundle, and the resource bundle is associated to the view object.

31.9.1.2 Using the format() Function in Groovy Expressions

To use the translatable string in the Groovy expression for Title, Body, and Keywords, you can write a Groovy expression to call the formatter.format() function with the key to the resource bundle entry as an input parameter. The other input parameters are standard Groovy input parameters.

For example, if the EMPVIEW_BODY translatable string is defined as:

EMPVIEW_BODY=Identification Number: {0}. {1} {2} {3} {4} {5} {6}

then you can use the following Groovy expression for the Body property:

formatter.format("EMPVIEW_BODY", Deptno, Empno ((DeptView.size() > 0) ? "Dept:" : ""), DeptView.Dname, ((StatesEAView.size() > 0) ? "State:" : ""), StatesEAView.Name, TestTransient)

The resource bundle key name, as well as up to ten parameters are added to the formatted string.

For date attributes, you can write a Groovy expression to call formatter.format(String attributeName), without a resource bundle key as an input parameter, to correctly format the date attribute value as part of the Groovy expression.

31.9.1.3 Associating Translated Labels to Attributes

In order for an attribute's label to be displayed in the correct locale, you must associate a resource bundle entry to the attribute.

To add labels to attributes:

  1. In the overview editor, select the Attributes navigation tab.

  2. Select the desired attribute in the Attributes table and click the Edit selected attribute(s) icon.

  3. In the Edit Attribute dialog, select Control Hints to open the Control Hints page.

  4. Click the icon next to the Label Text field to open and use the Select Text Resource dialog.

  5. Select a resource bundle from the Resource Bundle dropdown menu.

    Note:

    If there is no resource bundle associated with the view object, then an external resource bundle is created in the application. Save the new resource bundle externally.
  6. In the Display Value field, enter a string to associate with the key in the page's resource bundle.

  7. In the Key field, enter a string to uniquely identify a locale-specific object in the resource bundle.

  8. In the Description field, enter a description of any length for the key and value pair.

  9. Click Save and Select.

    A new entry is added to the resource bundle, and the resource bundle is associated to the view object attribute.

31.9.1.4 Using the getLabel() function in Groovy Expressions

In order for the translated value of an attribute label to be crawled in the correct locale, the function getLabel() must be called as part of the Groovy expression for Title, Body, and Keywords, as shown in Figure 31-7.

Figure 31-7 Example of Calling the getLabel() Function

Example of calling the getLabel() function

In the example, formatter.getLabel("Ename") retrieves the translated value of the attribute's label from the view object definition.

Calling the getLabel() function retrieves the translated value of the attribute's label from the view object definition for crawling.

31.9.2 How to Localize Facet Display Names

If not previously loaded for the current user's locale, facet display values are loaded by querying the list of values (LOV) associated with the facet in the context of the current user at query time. Whatever values or display values returned by querying the LOV in the context of the current user are loaded and returned as part of the facet in the query result for display in the user interface.

There are many ways to configure LOVs for localization. Two of them are described here: using the VL table and using resource bundles.

31.9.2.1 Configuring LOVs for Localization Using the VL Table

Facets are displayed in a tree.

Facet Display Name
        Facet Entry Display Name
        Facet Entry Display Name
        ...

The facet display name is translated per the resource bundle supported by Oracle Application Development Framework (Oracle ADF). The facet entry display names are translated per the LOV definition with context locale binding.

For example:

Organization
        Vision USA
        Vision Canada

Organization is translated via the Oracle ADF resource bundle with key ORGANIZATION_ID.

Select Text Resource dialog showing the key

To translate Vision USA and Vision Canada, you need to create a view object based on the VL table that supports localization, for example, HR_ALL_ORGANIZATION_UNITS_VL.

Query page showing the VL table

After this view object is created and used as the LOV for the BUSINESS_UNIT_ID of the base searchable object, the value for the field name is pulled from the VL table with the correct locale of the current user. For information on how to create facets, see Section 27.4.4, "How to Implement Faceted Navigation".

When your facet is localized, Oracle Fusion Applications Search UI requests facet definitions from ECSF when users performs a search. ECSF will load the facet definition, including facet names from the resource bundle based on user's locale. When the user clicks the facet, ECSF will execute the LOV for the facet and retrieve the data from the database based on the current user's language setting. In the example, the organization name will be displayed to the user in his language. When the user clicks on a particular node, such as Vision USA, ECSF will perform a query against the search engine with a filter on Organization Id = 204 where 204 is the organization ID for Vision USA.

31.9.2.2 Configuring LOVs for Localization Using the Resource Bundles

Facet display names can be localized with Oracle ADF resource bundles. When you create a facet, specify the name of a facet by referring to a resource key. At runtime, when the resource bundle is translated, the correct display name will be retrieved based on the user's locale. Localization of facet entries are supported by view object based LOVs. The view object must be designed to support localization.

Note:

Since facets are cached for performance, internationalized messages must be cached with the locale.

If the display name is not available for the current locale, the facet name is used.

ECSF uses LOVs to support facets, and the LOV display values must be translatable. The data sources of the LOVs used for facets can be view objects with static data or view objects with dynamic data (pulled from database through SQL). Display values of LOVs whose data sources are view objects with static data are displayed using the resource file corresponding to the application's current locale. Display values of LOVs populated with dynamic content can be translated based on the locale in the user's context.

You can translate the LOV display values based on the locale in the user's context by adding a language code column to the view object. The following instructions are based on the example provided in Section 27.4.4, "How to Implement Faceted Navigation". It assumes that you have an EmpView base view object with a working LOV on the StatesView view object already defined.

To translate LOV display values:

  1. Add a LANG column (VARCHAR2) to the STATES table.

  2. Populate the LANG column for all rows with a 2-character ISO639 code (for example, en or fr). For information, see http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt.

  3. Add the LANG column to the States entity object and StatesView view object. Alternatively, you can re-create it from scratch.

  4. Create a view criteria on StatesView (on the view object, navigate to Query > View Criteria > + and click the Add Criteria button) with the following values:

    • Attribute: Lang

    • Operator: equal to

    • Operand: Bind Variable

  5. For Parameter, click New to create a bind variable with the following values:

    • Name: UserLang

    • ValueType: Expression

  6. Click OK, and click OK again.

  7. Navigate to EmpView > View Accessors, select StatesView1, and click Edit.

  8. In the Edit View Accessor dialog, select the view accessor (StatesViewCriteria) in the Available list and click the Move icon to add it to the Selected list.

  9. Edit the UserLang bind variable from StatesView by entering the following Groovy expression that evaluates to the user's current language:

    adf.context.locale.getLanguage()
    

    The view criteria (similar to a defined WHERE clause) on StatesView and a bind variable named UserLang are created. The EmpView base view object is configured to use the view criteria when querying StatesView and the value of the bind variable is set to the user's current language. During runtime, only the records where LANG matches the locale.getLanguage() of the locale is returned.

31.9.3 How to Localize Crawl Management Display Names

Crawl management display names—that is, the searchable object names, search category names, and index schedule names displayed during crawl management—must be translated to the language of the administration tool user. Enabling translation for these display names is handled in the database through the translation table so that it can be shared across runtime, the ECSF Command Line Administration Utility, and Fusion Applications Control.

Note:

For Fusion Applications Control, the display name for searchable objects, search categories, and index schedules is in the language of the object created by the administration users.

Deployment to Oracle SES is not based on translated fields.

Localize the crawl management display names by translating the seed data that you create in English. For information see Chapter 55, "Initializing Oracle Fusion Application Data Using the Seed Data Loader".

31.9.4 How to Localize Crawlable Dynamic Content

Content crawled into Oracle SES can support different languages. For searchable objects with dynamic content (content pulled from a database through view objects), ECSF supports one language per business object instance. An example is a sales order created by a customer with a particular language. In other words, ECSF only supports language on a per record or row basis.

While ECSF supports only one language per object instance, each instance can be a different language. ECSF detects the language for a given document. For information, see Section 31.9.6, "How to Determine Locale".

31.9.5 How to Localize Crawlable Template Content

In addition to dynamic content, searchable objects can contain template content. Templates are text formats you create to form the body, title, keywords, action titles, parameters, and so on. In ECSF, you use Groovy scripting language to create expressions for some fields. The language must be identified in order to translate Groovy-enabled fields into the appropriate language before they are sent to the search engine. Localization for Groovy-enabled fields is supported by formatting functions.

ECSF provides a set of formatting functions based on Java Text Format Function. These formatting functions allow you more flexibility to build your desired formats. Example 31-29 illustrates the use of a Groovy function with Java Text Formatting to form the body for localization.

Example 31-29 Using Groovy Function to Translate the Body

formatter.format("EMPLOYEE_TEMPLATE", FirstName, LastName, EmailAddress, ZipCode)
for (item in doc.getChildDocs("Reports"))
{
   formatter.format("REPORTS_TEMPLATE", item.fields.FIRST_NAME, item.fields.LAST_NAME);
}

EMPLOYEE_TEMPLATE and REPORTS_TEMPLATE are keys to two translatable templates that are stored in a resource bundle. The template must be a legal format string for java.text.MessageFormat. (FirstName, LastName, EmailAddress, and ZipCode refer to view object attributes.)

To illustrate how the expression is resolved during runtime, see Table 31-2 for a sample view object, Table 31-3 for a sample child view object, and Example 31-30 for a sample resource bundle.

Table 31-2 Sample Parent View Object (Employee)

ID FirstName LastName EmailAddress ZipCode

1

John

Wayne

john.wayne@example.com

94065

2

Chris

Smith

chris.smith@example.com

94066


Table 31-3 Sample Child View Object (Reports)

ParentID FIRST_NAME LAST_NAME

1

John

Doe

1

Jake

Ray

2

Bob

Barley

2

Chris

Carpenter


Example 31-30 Sample Resource Bundle

id=EMPLOYEE_MESSAGE,message="Employee {0} {1} with email address {2} works in zipcode {4}"
id=REPORTS_TEMPLATE,message=" direct reports {0} {1}"

Note:

This example is strictly used to illustrate functionality. Concatenation of words using two resource messages is not translation friendly.

The following shows the resulting data sent to Oracle SES during runtime:

Employee John Wayne with email address john.wayne@example.com works in zipcode 94065 direct report named John Doe has a direct report named Jake Ray

Employee Chris Smith with email address chris.smith@example.com works in zipcode 94066 direct report named Bob Barley has a direct report named Chris Carpenter

Resource bundles are used to store language variations, while translation is the responsibility of Oracle Fusion Applications i18n support infrastructure.

31.9.6 How to Determine Locale

To display messages in the appropriate language for the user, ECSF must determine the locale. How ECSF determines locale varies by module.

31.9.6.1 Search Page

The Search page, an extension of Oracle JDeveloper, uses the locale of the Java Virtual Machine (JVM). This locale is set at either the machine level or at JDeveloper level. For information about setting the JVM locale for JDeveloper, see the JDeveloper online help.

31.9.6.2 ECSF Command Line Administration Utility

The ECSF Command Line Administration Utility, a standalone and single user application, uses the default locale returned by Locale.getDefault().

31.9.6.3 Crawl

You can specify the locale of the data to be indexed by using any of the following methods:

  • Language field

  • Crawler's language preference

  • Java Virtual Machine (JVM) default locale

You can specify a view object attribute as a language field for a searchable object by using the Search page. For information, see Section 27.2.3.1, "Setting Search Property Values for View Objects".

The value of this field for each instance is the language for the instance.

If you do not specify a language field for a given searchable object, ECSF uses the language preference of the crawler user. This implementation is application specific. ECSF obtains a session user through identity manager, and obtains the locale for the user while crawling data. There must be a crawler user with a proper locale profile, including language preference, created for Oracle Fusion Applications. This user's language preference is used as the default language if a language field does not exist.

If no crawler language is available, JVM's default locale (Locale.getDefault()) is used to identify the language, and the JVM default language is used.

31.9.6.4 Query

The user who calls the ECSF query time API must bind the locale to SearchContext. If the locale is not set in the search context, ECSF attempts to get from ADFContext. If that fails, the default locale, shown in Example 31-31, is used.

Example 31-31 Default Locale for Searcher

ADFContext adfContext = ADFContext.getCurrent();
Locale mLocale = (adfContext != null && adfContext.getLocale() != null) ? adfContext.getLocale() : Locale.getDefault();

31.10 Troubleshooting ECSF

This section describes common problems that you might encounter when using ECSF and explains how to solve them.

31.10.1 Problems and Solutions

The following are common problems you may encounter and solutions that solve them:

31.10.1.1 Cannot Remove the ECSF Runtime Server Library

You remove the ECSF Runtime Server library from the project and save, but the library reappears in your project.

Problem

The Common-Model.jar file that you added to your project contains a dependency on ECSF Runtime Server.

Solution

Remove the Common-Model.jar file.

31.10.1.2 Cannot See Data in Data Feeds

Your feed request made through either Oracle SES or the browser returns no data.

Problem

After you initially crawl the data source, subsequent feed requests result in incremental feeds (that is, feeds that contain only changes in data).

Solution

Override the incremental feed by performing one of the following tasks:

  • Through Oracle SES, start full indexing from the Fusion Applications Control. For information, see the "Starting Full Indexing" section in Oracle Fusion Applications Administrator's Guide.

  • Through the browser, append ?forceInitialCrawl=true to the config feed.

31.10.1.3 Configuration or Data Feed Execution Thread Is Busy for Longer than the Configured Warning Timeout

You receive an error indicating that the execution time of the configuration or data feed execution thread is exceeding the timeout settings, for example:

Error: name has been busy for "elapsedTime" seconds working on the request "curReq", which is more than the configured time (StuckThreadMaxTime) of "maxTime" seconds.

Following are two possible causes and solutions for this issue:

Problem

A SQL script being executed by ECSF for the configuration feed or data feed is taking a long time to execute.

Solution

Enable SQL tracing to find long-running SQL processes, and tune the SQL script to reduce the execution time.

Problem

ECSF is unexpectedly running longer than the configured time.

Solution

Increase the Oracle WebLogic Server warning timeout setting.

31.10.1.4 Class Not Found Errors When Running the ECSF Servlet

You receive Class Not Found errors when you run the ECSF servlet.

Problem

When you created the ECSF application, you did not select the Fusion Web Application (ADF) template.

Solution

Add the Fusion Web Application (ADF) template through Application Properties.

31.10.1.5 Out of Memory Error when Deploying the ECSF Application to Oracle WebLogic Server or Running the Application

You receive a java.lang.OutOfMemoryError: PermGen space exception when you deploy the ECSF application to Oracle WebLogic Server instance or when you run the application.

Problem

MaxPermSize is set too low.

Solution

Increase MaxPermSize by starting the WebLogic JVM using the -XX:MaxPermSize=256m parameter.

If you start the ECSF servlet from within JDeveloper using the Integrated WebLogic Server:

  1. Go to the jdev cache directory /.jdeveloper/DefaultDomain/bin.

  2. Open setDomainEnv.sh.

  3. Locate the line that contains -XX:MaxPermSize=128m, and change it to -XX:MaxPermSize=256m.

31.10.1.6 Blank Oracle ADF/UI Shell Pages

Oracle ADF/UI Shell pages fail to load or a blank page appears. You receive the following exception in the JDeveloper Log window:

java.lang.NoClassDefFoundError: oracle/ecsf/client/SearchCtrl at oracle.apps.fnd.applcore.globalSearch.ui.ecsf.ECSFSearchUtils.getSearchControl(ECSFSearchUtils.java:67)

Problem

The ECSF Client library is missing from the project class path.

Solution

Update the project class path with the ECSF Client library.

31.10.1.7 Memory Leak on ThreadLocal Variable (SearchContext)

You encounter memory leaks while using the ECSF query API to define the Oracle Fusion Applications Search user interface.

Problem

SearchContext is implemented as a ThreadLocal variable, which is created when ContextFactory.getSearchContext() is first called in the current Thread. All subsequent calls to getSearchContext() within the same Thread results in that instance being returned. Since the ThreadLocal variable remains associated with the Thread, memory leaks occur.

Solution

After the completion of your Oracle Fusion Applications Search UI logic and before results are returned to the UI for rendering, you must call the SearchContext.release method.

When the SearchContext.release method is called on the SearchContext, the instance of the ThreadLocal is removed from the current Thread. A subsequent call to getSearchContext then results in the creation of a new instance of the SearchContext ThreadLocal variable. The SearchContext.release method also implicitly call releaseConnection().

Note:

If you call SearchContext.release and then need to call getSearchContext() again within the same Thread, then you must set any parameters you need on the SearchContext again as it is a brand new instance.

31.10.2 Diagnosing ECSF Problems

To diagnose ECSF problems in the development environment, you can view the log messages in the Oracle WebLogic Server at DOMAIN_HOME/servers/SERVER_NAME/logs/SERVER_NAME-diagnostic.log.

You can configure the log level for ECSF by using Fusion Applications Control or Oracle Weblogic Scripting Tool. For information, see the "Configuring Settings for Log Files" chapter in the Oracle Fusion Middleware Administrator's Guide.

The log level for ECSF can be set to the following values:

  • TRACE for FINE

  • NOTIFICATION for INFO (default level)

  • WARNING for WARNING

  • ERROR for SEVERE

The following ECSF loggers can be used for logger name:

  • oracle.ecsf.AdminLogger

  • oracle.ecsf.ClientLogger

  • oracle.ecsf.RuntimeLogger

31.10.3 Need More Help?

For additional assistance, you can send your inquiries to HELPECSF_US@oracle.com.