Sitemap

Wednesday, May 6, 2015

Application Extension Framework (AXF)

This is the intermediary between the Oracle application and UCM. AXF is a web service call for Oracle apps. AXF performs one RIDC call to UCM to check for access, then responds to the app. It runs in IPM, deployed in IPM and uses IPM database tables.

In EBS menu, go to Payables - Invoices - Invoices link to open the Forms application. A saved invoice looks like this when opened.



What Does AXF do?


IPM sends a SOAP response to EBS with a UCM URL. If successful, EBS opens a browser to that URL. UCM login screen opens in browser, and then after login, Managed Attachments opens. User works with UCM in browser (independently of EBS). Standard service calls are made to UCM. There is no SOAP or RIDC used at this point.


Clicking New button pops up a checkin form specific to the integration:


Private Attachments vs. Shared Attachments

Private Attachments:
1. "Share document" option unchecked during checkin.
2. Security Group = AFDocuments
3. Not searchable in standard UCM search for anyone (except for users with specific roles, e.g. AFWrite, AFAdmin, etc).

Shared Attachments
1. "Share document" checked.
2. Allows Security group to be selected from groups that user has access to.


Enabling Managed Attachments

Enabling Managed Attachments requires certain components to be enabled:
• AppAdapterCore – Needed for all AXF integrations to UCM.
• AppAdapterEBS – Needed just for EBS to UCM integration.
• AppAdapterPSFT – Needed for PeopleSoft to UCM integration.

Other components that must be enabled:
• YahooUserInterfaceLibrary
• CheckoutAndOpenInNative
• CoreWebdav
• NativeOsUtils
• ContentFolios


How does UCM secure and separate regular content from private attachments?

• By security group: AFDocuments
• By database tables:
  • AFGrants – Grants user access privileges.
  • AFObjects – links the content items (attachments) to the invoice (business object)

When IPM calls AF_GRANT_ACCESS service, a row is written to UCM table AFGrants. Temporary access (half hour by default) granted to a user.

• dUsername: EBS/UCM user of same name. (e.g. operations)
• dAFApplication: Identifier for the instance of EBS. (VIS121)
• dAFBusinessObject: Identifier for the EBS form (e.g. invoice 1234)
• dAFBusinessObjectType: What kind of EBS entity is it? Invoice.
• dPrivilege: Defaults to Write from AppAdapterGrantPrivilege=W
• dExpirationDate: Defaults to AppAdapterGrantHours=.5 (in hours)

AFObjects contains "attachment" information, linking content items to EBS entities such as invoices.

• dAFApplication: Identifier for the instance of EBS. (VIS121)
• dAFBusinessObjectType: What kind of EBS entity is it? Invoice.
• dAFBusinessObject: Identifier for the EBS form (e.g. invoice 1234)
• dDocName: Content ID


Default Content Server profiles are provided for each business application,
• EBSProfile for Oracle E-Business Suite
• PSFTProfile for Oracle PeopleSoft.


AXF_SOLUTION_PARAMETERS Table
In IPM schema (DEV_IPM), the table AXF_SOLUTION_PARAMETERS holds information about the UCM server.


• RIDC_CONNECTION_STRING: used to call AF_GRANT_ACCESS service.
• UCM_CONNECTION_STR is used to build the URL to pass back to the requesting application (e.g. EBS).

OracleTextSearch Thesaurus Search

Configure OracleTextSearch for UCM
SearchIndexerEngineName=OracleTextSearch
IndexerDatabaseProviderName=SystemDatabase
AdditionalEscapeChars=-:#

Place the thesaurus file in a proper location, and then execute the below command in the terminal
ctxload -user USERNAME/PASSWORD -thes -name cbi_thesaurus -file NAME_OF_FILE

where
     USERNAME: username of the database
     PASSWORD: password for the database
     NAME_OF_FILE: name of the thesaurus file

In order to check whether the contents of the file are properly loaded or not, execute the queries in SQL Developer:

1. The following query will display the names of the thesaurus (in this case, only CBI_THESAURUS)
select * from CTX_THESAURI

2. The following query will display the list of the words that were loaded in the thesaurus:
select * from CTX_THES_PHRASES

This is the Java method:

    public void cbiThesaurusSearch() throws DataException, ServiceException, IOException {
        SystemUtils.trace(COMPONENT_DEBUG, "=====CBIThesaurusSearch STARTS=====");

        String searchKeyword = "";
        String searchKeyword2 = "";
        String queryText = "";

        SystemUtils.trace(COMPONENT_DEBUG, "Original QueryText: " + m_binder.getLocal("QueryText"));
        try {
            if (m_binder.getLocal(SEARCH_KEY) != null) {
                searchKeyword = m_binder.getLocal(SEARCH_KEY).toUpperCase();
                SystemUtils.trace(COMPONENT_DEBUG, "Search Keyword:" + searchKeyword);
                
                

                if (m_binder.getLocal(SEARCH_KEY_WITHIN) != null && (m_binder.getLocal(SEARCH_KEY_WITHIN).length() > 0)) {
                    searchKeyword2 = m_binder.getLocal(SEARCH_KEY_WITHIN).toUpperCase();
                    SystemUtils.trace(COMPONENT_DEBUG, "Search Keyword Within:" + searchKeyword2);
                }

                int index = m_binder.getLocal("QueryText").indexOf("(");
                queryText = m_binder.getLocal("QueryText").substring(0, index);
                SystemUtils.trace(COMPONENT_DEBUG, "Altered QueryText: " + queryText);

                DataBinder db = new DataBinder();
                db.putLocal("searchKey", searchKeyword);
                db.putLocal("thName", THESAURUS_NAME);
                db.putLocal("level", LEVEL);

                String expandedKeywords = "";

                ResultSet rs = m_workspace.createResultSet("CBIThesaurusSearch", db);
                DataResultSet dataContainer = new DataResultSet();
                dataContainer.copy(rs);
                for (dataContainer.first(); dataContainer.isRowPresent(); dataContainer.next()) {
                    String expandedKeyword = dataContainer.getStringValueByName("THEVALUES");
                    expandedKeywords = expandedKeywords + expandedKeyword + ",";
                }
                if (expandedKeywords.endsWith(",")) {
                    expandedKeywords = expandedKeywords.substring(0, expandedKeywords.length() - 1);
                }
                SystemUtils.trace(COMPONENT_DEBUG, "ExpandedKeywords: " + expandedKeywords);

                if (searchKeyword2 == "") {
                    m_binder.putLocal("QueryText", queryText + "(" + expandedKeywords + ")");
                } else {
                    m_binder.putLocal("QueryText",
                                      queryText + "((" + expandedKeywords + ")  (" + searchKeyword2 + "))");
                }

                SystemUtils.trace(COMPONENT_DEBUG, "Final QueryText: " + m_binder.getLocal("QueryText"));


                //m_binder.putLocal("IdcService", "GET_SEARCH_RESULTS");
                m_binder.putLocal("IdcService", "CBIDispSortSearch");
                executeService(m_binder, "sysadmin", false);
            }
        } catch (ServiceException s) {
            SystemUtils.trace(COMPONENT_DEBUG, "ServiceException " + s.getMessage());
        } catch (DataException d) {
            SystemUtils.trace(COMPONENT_DEBUG, "DataException " + d.getMessage());
        } catch (Exception e) {
            SystemUtils.trace(COMPONENT_DEBUG, "Exception " + e.getMessage());
            e.printStackTrace();
        } finally {
            m_workspace.releaseConnection();
        }
        SystemUtils.trace(COMPONENT_DEBUG, "=====CBIThesaurusSearch ENDS=====");
    }


And this is the query which is being called by the method:
SELECT DISTINCT INITCAP (val) THEVALUES
  FROM (SELECT *
          FROM (    SELECT REGEXP_SUBSTR (
                              (SELECT ctx_thes.syn (?, ?)
                                 FROM DUAL),
                              '[^{|}]+',
                              1,
                              LEVEL,
                              'i')
                              val
                      FROM DUAL
                CONNECT BY LEVEL <=
                              REGEXP_COUNT (
                                 (SELECT ctx_thes.syn (?, ?)
                                    FROM DUAL),
                                 '[^|]+'))
         WHERE val IS NOT NULL
        UNION
        SELECT *
          FROM (    SELECT REGEXP_SUBSTR (
                              (SELECT ctx_thes.bt (?, ?, ?)
                                 FROM DUAL),
                              '[^{|}]+',
                              1,
                              LEVEL,
                              'i')
                              val
                      FROM DUAL
                CONNECT BY LEVEL <=
                              REGEXP_COUNT (
                                 (SELECT ctx_thes.bt (?, ?, ?)
                                    FROM DUAL),
                                 '[^|]+'))
         WHERE val IS NOT NULL
        UNION
        SELECT *
          FROM (    SELECT REGEXP_SUBSTR (
                              (SELECT ctx_thes.nt (?, ?, ?)
                                 FROM DUAL),
                              '[^{|}]+',
                              1,
                              LEVEL,
                              'i')
                              val
                      FROM DUAL
                CONNECT BY LEVEL <=
                              REGEXP_COUNT (
                                 (SELECT ctx_thes.nt (?, ?, ?)
                                    FROM DUAL),
                                 '[^|]+'))
         WHERE val IS NOT NULL)

      searchKey varchar
      thName varchar
      searchKey varchar
      thName varchar
      searchKey varchar
      level int
      thName varchar
      searchKey varchar
      level int
      thName varchar
      searchKey varchar
      level int
      thName varchar
      searchKey varchar
      level int
      thName varchar

Encryption and Decryption with Bouncy Castle

Portable PGP is a fully featured lightweight java based PGP tool. It allows to encrypt,decrypt,sign and verify text and files with a nice and absolutely straight graphical interface.

Download from the link below:
http://sourceforge.net/projects/ppgp/

Barcode Recognition with Google's ZXing

1. Get the core and javase jars from the link below:
http://central.maven.org/maven2/com/google/zxing/

2. Create a Java method:

public class ZxingTest {
    public static void main(String[] args) throws IOException {

        File imageFile = new File("test.png");
        BufferedImage image = ImageIO.read(imageFile);

        try {
            LuminanceSource source = new BufferedImageLuminanceSource(image);

            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            Reader reader = new MultiFormatReader();
            Result result = reader.decode(bitmap);

            System.out.println("Barcode text: " + result.getText());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

NOTE: This doesn't work for the TIFF files

Download the file Zxing.rar from the file cabinet the get the entire project.

IdcCommand

Create a file named "pingserver.hda" and add the following lines to it:
@Properties LocalData
IdcService=PING_SERVER
@end

Open a command prompt and change to your <working_dir>\<instance>\ucm\cs\bin directory (for example, cd C:\working_dir\development\ucm\cs\bin).

Issue the following command:
IdcCommand -f C:\pingserver.hda -u sysadmin -l C:\pingserver.log -c server

Confirm the output.

More info: http://docs.oracle.com/cd/E29542_01/doc.1111/e26694/idc.htm#WCCDV334

Saturday, April 11, 2015

UCM: Create a resultset and store it in HDA file

SCENARIO: I will create a custom resultset and save it in the form of HDA file in a particular location. I can later access the table and retrieve the data. I hope the code is self-explainable.


public class CreateHDAFile extends Service {

    public static final String HDAFILENAME = "hdafile.hda";
    public static final String CUSTOMRESULTSET = "CustomResultSet";
    public static final String CLASS_NAME = "CreateHDAFile";
    public static final String DIRECTORY = DirectoryLocator.getAppDataDirectory() + "test/";

    public void createHDAFile() throws ServiceException, DataException {
        String query = "SELECT r.did, r.ddocname, R.DDOCTITLE FROM revisions r, docmeta d WHERE r.did = d.did";
        ResultSet resultSet = m_workspace.createResultSetSQL(query);
        DataResultSet dataResultSet = new DataResultSet();
        dataResultSet.copy(resultSet);
        m_binder.addResultSet("InputDataHDAFile", dataResultSet);
        saveDataResultSet(m_binder);
    }

    public static void saveDataResultSet(DataBinder data) throws ServiceException {
        trace(DIRECTORY);
        FileUtils.checkOrCreateDirectoryEx(DIRECTORY, 0, true);
        FileUtils.reserveDirectory(DIRECTORY);
        try {
            ResourceUtils.serializeDataBinder(DIRECTORY, HDAFILENAME, data, true, true);
        } finally {
            FileUtils.releaseDirectory(DIRECTORY);
        }
        DataResultSet drset = (DataResultSet) data.getResultSet("InputDataHDAFile");
        SharedObjects.putTable(CUSTOMRESULTSET, drset);
    }
}

These are the contents of the HDA file:

@ResultSet InputDataHDAFile
3
dID 3 38
dDocName 6 30
dDocTitle 6 255
1
LTSCHAUDHARYHY000001
Test
201
HELLO
HELLO
202
SMILEY
SMILEY
@end

Now to access the contents of the HDA file:

DataBinder binder = new DataBinder();
binder = ResourceUtils.readDataBinder(DIRECTORY, HDAFILENAME);
DataResultSet savedMap = (DataResultSet) binder.getResultSet("InputDataHDAFile");

UCM: Custom Service Class

A custom service class extends the content server’s core intradoc.server.Service.

The Service super class does not contain any methods that should be called directly. It mainly has support functions for
• Running service actions in the correct order
• Initializing a user’s security
• Running database queries
• Creating the context for the request

Key Service Class Objects
Variable Class Description
m_workspace intradoc.data.Workspace The database connection
m_binder intradoc.data.DataBinder The request and response data
m_currentAction intradoc.data.Action The current service action
m_serviceData intradoc.data.ServiceData The current service’s definition
m_userData intradoc.data.UserData The user running the service
m_service intradoc.data.Service A reference to the parent service object

The predefined Service class object m_binder has the same functionality as &IsJava=1

This is how a service class or service handler can access a parameter being passed from the service action:
String param = m_current_action.getParamAt(0);

This will get the first parameter.

Service class methods that are called from a service have a required signature: public void myMethod() throws DataException, ServiceException;
public class AcmeMailService extends Service {

    public void sendMail() throws DataException, ServiceException {
        String str = m_binder.getLocal("acmeEmailAddresses");
    }
}


Service classes:
• Service classes require minimal registration inside the server.
• Methods of a service class can only be used as actions in a service associated with that service class.
• Methods of a custom service class cannot be used as actions when extending standard services.

Service handlers
• Service handlers require additional registration inside the server.
• Methods of a service handler can be used as actions in services associated with different service classes.
• Methods of a custom service handler can be used as actions when extending standard services.