Skip to content
August 30, 2012 / Mário Maçarico

Developing Components – Adding a custom idoc script

Today I will try to explain how to make a ucm component.

This component will add new, custom idoc script that will fetch a value from a database table. I will name this component DefaultValuesComponent.

I used Eclipse and Maven to make the component.

In the end the project structure was the following:

The resources folder contained:

  •  defaultvaluescomponent_extensions.html – This file is used to integrate java code with custom idoc script, this is where we will map our tag “getDefaultValue()” with the actual java code.
  • defaultvaluescomponent_query.html – This file contains our queries to the database. In this case we will use it internaly in the “getDefaultValue()” java code.
  • DefaultValuesComponent.hda – The file that tell ucm what and how to use the resources for this component.
  •  manifest.hda – required file that specifies the location of the component file and other entryTypes such as componentClasses.

Other files

  • pom.xml – Maven file with the necessary dependencies and plugins.
  •  assembly.xml – Used to package the component into a zip with the right structure.

Let’s see the DefaultValuesComponent.hda:

<?hda version="11gR1-11.1.1.4.0-idcprod1-101229T001824" jcharset=UTF8 encoding=utf-8?>
@Properties LocalData
blFieldTypes=
preventAdditionalComponentDowngrade=0
hasPreferenceData=0
blDateFormat=dd/MM/yyyy {H:mm[:ss] [zzz]}!tAmerica/Los_Angeles
version=${buildDate} (build ${componentVersion})
ComponentName=DefaultValuesComponent
@end
@ResultSet ClassAliases
3
classname
location
loadOrder
@end
@ResultSet Filters
4
type
location
parameter
loadOrder
@end
@ResultSet ResourceDefinition
4
type
filename
tables
loadOrder
resource
resources/defaultvaluescomponent_extensions.htm
DefaultValuesScriptsExtensions
1
query
resources/defaultvaluescomponent_query.htm
DefaultValuesComponent_Queries
10
@end
@ResultSet MergeRules
4
fromTable
toTable
column
loadOrder
DefaultValuesScriptsExtensions
IdocScriptExtensions
name
1
@end

Here is defined two ResourceDefinitions, one of the type “resource” and other as “query”.

The first is the actual idoc script component / java mapping, defined as a ‘resource’ in the file ‘resources/defaultvaluescomponent_extensions.htm’ using the table ‘DefaultValuesScriptsExtensions’.

The second is the query that we will use to get de value from the database. it is defined as ‘query’ in the file ‘resources/defaultvaluescomponent_query.htm’ using the table ‘DefaultValuesComponent_Queries’.

Finally we must add our ‘DefaultValuesScriptsExtensions’ mapping to UCM ‘IDocScriptExtensions’. Without this UCM will not recognize the tag.

The query file is as follows, nothing major, just a select to a table with two parameters. The query was named ‘QDefaultValuesByFormularioAndMetadado’:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>
DefaultValuesComponent query</head>
<body>

<@table DefaultValuesComponent_Queries@>
<table border=1><caption><strong>Query Definition Table</strong></caption>
<tr>
<td>name</td><td>queryStr</td><td>parameters</td>
</tr>
<tr>
<td>QDefaultValuesByFormularioAndMetadado</td>
<td>select * from FORMULARIO_DEFAULTS F where F.FORMULARIO=? and F.METADADO = ?</td>
<td>xIdcProfile varchar
qMetadado varchar</td>
</tr>
</table>
<@end@>
</body>
</html>

The extensions file objective is only one, to map idoc script to a java class. In this case we map to the class ‘pt.link.ucm.defaultvaluescomponent.script.DefaultValuesScriptsExtensions’:


<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<TITLE></TITLE>
</HEAD>
<BODY>
<!-- This table is required to integrate the custom Java code for
 customized IdocScript.

 The 'name' value should be unique to the component. Otherwise, if
 one of the standard names is used, the class loaded here will override
 the functionality of that class. -->

<@table DefaultValuesScriptsExtensions@>
<table border=1><caption><strong>DefaultValuesScripts Extensions
 </strong></caption>
<tr>
 <td>name</td><td>class</td><td>loadOrder</td>
</tr>
<tr>
 <td>DefaultValuesScriptsExtensions</td>
 <td>pt.link.ucm.defaultvaluescomponent.script.DefaultValuesScriptsExtensions</td>
 <td>10</td>
</tr>
</table>
<@end@>

The actual java is where the magic is done. The class must extend ‘ScriptExtensionsAdaptor’ and override the constructor and ‘evaluateFunction’ method.

The idoc script function name is defined on the variable ‘m_functionTable’. For each function, the variable ‘m_functionDefinitionTable’ must define its configuration such as input arguments and return arguments. See the comments on line 30 of the code.

You should only modify line 28 and 38 and the respective switch case to line 113 if you wish to add new idoc script functions.

package pt.link.ucm.defaultvaluescomponent.script;

import intradoc.common.ExecutionContext;
import intradoc.common.GrammarElement;
import intradoc.common.LocaleUtils;
import intradoc.common.Report;
import intradoc.common.ScriptExtensionsAdaptor;
import intradoc.common.ScriptInfo;
import intradoc.common.ScriptUtils;
import intradoc.common.ServiceException;
import intradoc.data.DataBinder;
import intradoc.data.DataException;
import intradoc.data.ResultSet;
import intradoc.server.Service;
import intradoc.server.script.ScriptExtensionUtils;
import intradoc.shared.UserData;

/**
* This class demonstrates how to create custom IdocScript functions. These
* include variable names that should be evaluated, variables that are either
* true or false, as well as new kinds of functions.
*/
public class DefaultValuesScriptsExtensions extends ScriptExtensionsAdaptor {
public DefaultValuesScriptsExtensions() {

// this is a list of the functions that can be called with the custom
// code
m_functionTable = new String[] { "getDefaultValue" };

// Configuration data for functions. This list must align with the
// "m_functionTable"
// list. In order the values are "id number", "Number of arguments",
// "First argument type",
// "Second argument type", "Return Type". Return type has the following
// possible
// values: 0 generic object (such as strings) 1 boolean 2 integer 3
// double.
// The value "-1" means the value is unspecified.
m_functionDefinitionTable = new int[][] { { 0, 1, GrammarElement.STRING_VAL, -1, 0 }, // getDefaultValue
};
}

/**
* This is where the custom IdocScript function is evaluated.
*/
public boolean evaluateFunction(ScriptInfo info, Object[] args, ExecutionContext context) throws ServiceException {
/**
* This code below is optimized for speed, not clarity. Do not modify
* the code below when making new IdocScript functions. It is needed to
* prepare the necessary variables for the evaluation and return of the
* custom IdocScript functions. Only customize the switch statement
* below.
*/
int config[] = (int[]) info.m_entry;
String function = info.m_key;

int nargs = args.length - 1;
int allowedParams = config[1];
if (allowedParams &gt;= 0 &amp;&amp; allowedParams != nargs) {
String msg = LocaleUtils.encodeMessage("csScriptEvalNotEnoughArgs", null, function, "" + allowedParams);
throw new IllegalArgumentException(msg);
}

String msg = LocaleUtils.encodeMessage("csScriptMustBeInService", null, function, "Service");
Service service = ScriptExtensionUtils.getService(context, msg);
DataBinder binder = service.getBinder();

UserData userData = (UserData) context.getCachedObject("UserData");
if (userData == null) {
msg = LocaleUtils.encodeMessage("csUserDataNotAvailable", null, function);
throw new ServiceException(msg);
}

// Do some initial conversion of arguments. Choices of what initial
// conversions to make
// are based on frequency of usage. If a function uses nontypical
// parameters it will
// have to do its own conversion.
String sArg1 = null;
String sArg2 = null;
long lArg1 = 0;
long lArg2 = 0;
if (nargs &gt; 0) {
if (config[2] == GrammarElement.STRING_VAL) {
sArg1 = ScriptUtils.getDisplayString(args[0], context);
} else if (config[2] == GrammarElement.INTEGER_VAL) {
lArg1 = ScriptUtils.getLongVal(args[0], context);
}

}
if (nargs &gt; 1) {
if (config[3] == GrammarElement.STRING_VAL) {
sArg2 = ScriptUtils.getDisplayString(args[1], context);
} else if (config[3] == GrammarElement.INTEGER_VAL) {
lArg2 = ScriptUtils.getLongVal(args[1], context);
}
}

/**
* Here is where the custom code should go. The case values coincide
* with the "id values" in m_functionDefinitionTable. Perform the
* calculations here, and place the result into ONE of the result
* variables declared below. Use 'sArg1' and 'sArg2' for the first and
* second String arguments for the function (if they exist). Likewise
* use 'lArg1' and 'lArg2' for the first and second long integer
* arguments.
*/
boolean bResult = false; // Used for functions that return a boolean.
int iResult = 0; // Used for functions that return an integer.
double dResult = 0.0; // Used for functions that return a double.
Object oResult = null; // Used for functions that return an object
// (string).
switch (config[0]) {
case 0: // getDefaultValue

String metafieldName = sArg1;

service.getBinder().putLocal("qMetadado", metafieldName);

if ((metafieldName != null) &amp;&amp; (!metafieldName.isEmpty())) {
ResultSet rs;
try {
rs = service.getWorkspace().createResultSet("QDefaultValuesByFormularioAndMetadado", service.getBinder());
} catch (DataException e) {
Report.error("DefaultValuesScript", "error executing query", e);
return false;
}
oResult = rs.getStringValueByName("VALOR_METADADO");
}

break;

default:
return false;
}

/**
* Do not alter code below here
*/
args[nargs] = ScriptExtensionUtils.computeReturnObject(config[4], bResult, iResult, dResult, oResult);

// Handled function.
return true;
}
}

Next run maven with goal “package” and a new zip file with the component will be build in the target directory.

This the pom file. It uses the assembly plugin to generate the component zip that will be used to deploy to ucm server. It also has a dependency to a ucm library. You must manualy add this file to your local maven repository since there is no central repository with this library.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>pt.link.ucm.component</groupId>
 <artifactId>default-values</artifactId>
 <version>0.0.1-SNAPSHOT</version>

 <name>DefaultValuesComponent</name>
 <url>http://maven.apache.org</url>

<prerequisites>
 <maven>2.2.1</maven>
 </prerequisites>

<build>
 <plugins>
 <plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-compiler-plugin</artifactId>
 <version>2.3.2</version>
 <configuration>
 <source>1.6</source>
 <target>1.6</target>
 </configuration>
 </plugin>

<plugin>
 <artifactId>maven-assembly-plugin</artifactId>
 <configuration>
 <descriptors>
 <descriptor>assembly.xml</descriptor>
 </descriptors>
 </configuration>
 <executions>
 <execution>
 <id>make-assembly</id>
 <phase>package</phase>
 <goals>
 <goal>single</goal>
 </goals>
 </execution>
 </executions>
 </plugin>
 </plugins>
 <resources>
 <resource>
 <directory>src/main/resources</directory>
 <filtering>true</filtering>
 </resource>
 </resources>
 </build>
 <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <maven.build.timestamp.format>yyyy_MM_dd</maven.build.timestamp.format>
 <buildDate>${maven.build.timestamp}</buildDate>
 <componentVersion>${version}</componentVersion>
 </properties>
 <dependencies>
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>3.8.1</version>
 <scope>test</scope>
 </dependency>
 <dependency>
 <groupId>com.oracle.ucm</groupId>
 <artifactId>idcserver</artifactId>
 <version>11.1.1.4.0</version>
 <scope>compile</scope>
 </dependency>
 </dependencies>

</project>

This is the assembly file:

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
 <id>bin</id>
 <formats>
 <format>zip</format>
 </formats>
 <includeBaseDirectory>false</includeBaseDirectory>
 <fileSets>
 <fileSet>
 <directory>${project.basedir}/src/main/resources</directory>
 <outputDirectory></outputDirectory>
 <includes>
 <include>manifest.hda</include>
 </includes>
 </fileSet>
 <fileSet>
 <directory>${project.basedir}/src/main/resources</directory>
 <outputDirectory>component/DefaultValuesComponent</outputDirectory>
 <includes>
 <include>DefaultValuesComponent.hda</include>
 <include>readme.txt</include>
 </includes>
 </fileSet>
 <fileSet>
 <directory>${project.basedir}/target/classes</directory>
 <outputDirectory>component/DefaultValuesComponent/classes</outputDirectory>
 <excludes>
 <exclude>*.htm</exclude>
 <exclude>*.hda</exclude>
 <exclude>*.txt</exclude>
 </excludes>
 </fileSet>
 <fileSet>
 <directory>${project.basedir}/src/main/resources</directory>
 <outputDirectory>component/DefaultValuesComponent/resources</outputDirectory>
 <includes>
 <include>*.htm</include>
 </includes>
 </fileSet>
 </fileSets>
</assembly>

Finally the manifest.hda to finish the component:

<?hda version="11gR1-11.1.1.4.0-idcprod1-101229T001824" jcharset=UTF8 encoding=utf-8?>
@Properties LocalData
CreateDate=7/12/11 7:11 PM
blFieldTypes=CreateDate date
ComponentName=DefaultValuesComponent
blDateFormat=M/d{/yy}{ h:mm[:ss]{ a}}!mAM,PM!tWET
@end
@ResultSet Manifest
2
entryType
location
component
DefaultValuesComponent/DefaultValuesComponent.hda
componentExtra
DefaultValuesComponent/readme.txt
componentClasses
DefaultValuesComponent/classes/
@end

Don’t forget to create the table and insert some values for it. Fot this example:


CREATE TABLE FORMULARIO_DEFAULTS
 (
 "ID" NUMBER(*,0) NOT NULL ENABLE,
 "FORMULARIO" VARCHAR2(100 CHAR),
 "METADADO" VARCHAR2(100 CHAR),
 "VALOR_METADADO" VARCHAR2(100 CHAR),
 CONSTRAINT "PK_FORMULARIO_DEFAULTS" PRIMARY KEY ("ID") ENABLE
 )

After installing the component you can use it as any other idoc script on ucm. For example, you can create a rule and use it to set a default value to a metafield:

<$dprDefaultValue=getDefaultValue("xProjectRef")$>
Advertisements

One Comment

Leave a Comment
  1. srikanth / Sep 11 2014 3:36 pm

    Hello Sir,
    Nice and helpful post. It would be great if you can actively write posts on developing custom components. Thanks in advance

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: