Archive for the ‘CSSDK’ Category

Here’s a quick example on how to do a regular expression matching in scala for a DCR

scala> val regexString="templatedata/category/type/data/(.*?)/(.*)"

regexString: java.lang.String = templatedata/category/type/data/(.*?)/(.*)

scala> val regex=regexString.r
regex: scala.util.matching.Regex = templatedata/category/type/data/(.*?)/(.*)

scala> val regex(language,file)=”templatedata/category/type/data/en_gb/mydcr”

language: String = en_gb
file: String = mydcr

scala> println(language)
en_gb

scala> println(file)
mydcr

If the file does not match the pattern, this happens:

scala> val regex(language,file)=”templatedata/category/type/data/en_gb”
scala.MatchError: templatedata/category/type/data/en_gb (of class java.lang.String)
at .<init>(<console>:11)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:679)

In order to avoid the exception, a quick check that it does match is in order:

scala> “templatedata/category/type/data/en_gb”.matches(regex.pattern.toString)
res4: Boolean = false

scala> “templatedata/category/type/data/en_gb/mydcr”.matches(regex.pattern.toString)
res5: Boolean = true

Oh boy. it’s late and I’ve been working quite hard lately, both on the current day job and lately with my side project which is what I wanted to share with everyone here and now.
I’ve started writing some training course material about the CSSDK in all its glory in the hope that someone out there would take me up on the offer and sign up for a training session.
It’s early stages yet, although I’ve already written 5 sections out of what I think will be about 10. This means I’m half way there (Hurray!) and that means I need to start broadcasting this to the whole wide world and start generating some interest. An interesting point is Interwoven Autonomy HP (?) does not seem to cover it in any great detail.
Up until now, I have written (besides the training plan):
  • a succinct introduction,
  • a chapter on how to configure the IDE with the TeamSite jars and JavaDocs,
  • a chapter on the CSFactory and getting connected to teamsite (locally and via SOAP, if you please),
  • a chapter on the common package, the CSClient and other assorted bits
  • a chapter on users, groups and roles and operations.

Impressions to this date on the work has been fairly positive. I’ve enjoyed writing it and I was surprised at how much there is to actually say about users and group, the biggest chapter to date. I’ve learnt a fair bit about the new features of the latest release and discovered a couple of tricks along the way. I can’t wait to share them!

I’ll post progress reports in the blog. That’ll keep me motivated to keep writing and also this will whet your appetite to want to learn more about what I’m currently doing. Some of you may think you don’t need this kind of training and you might be right, but can you answer this question quickly off the top of your head?

“Which objects would you use to add a user to a branch with the editor role using the CSSDK?”

The answer, as it happens, is in the training course so I wont actually give you it straight away. Oh, and if anyone from HP is reading this, you can buy my company any day of the week and improve your offering (ooh, controversial)!

Datasources allow mainly to select things from drop-down lists in forms publisher forms and workflow forms.

Datasources are nothing but java classes that get executed when a form loads. The class must implement one of the following interfaces:

com.interwoven.datasource.ListDataSource;
com.interwoven.datasource.MapDataSource;
com.interwoven.datasource.SimpleDataSource;
com.interwoven.datasource.SortedValuesMapDataSource;

As part of the implementation, the class must therefore have an execute method which gets passed a DataSourceContext object. The return value and type vary depending of which datasource class you implement.

A SimpleDataSource implementation returns a single value. Forms publisher will use the value for example in a text field.

public String execute(String sessionId, String context, Map params) {
String datasource="value";
return(datasource);
}

A ListDataSource implementation will return a List of strings. Forms publisher will use the same value for the form’s value and label.

public List<String> execute(DataSourceContext context) {
List<String> datasource = new ArrayList<String>();
datasource.add(“value”);
return(datasource);
}

The context contains some information about the session id, the server context (the vpath of the current node) and the other custom parameters passed to the datasource.

A MapDataSource implementation will return map of strings. Forms publisher will use one value for the label and the other for the value. The key is the value string.

public Map<String, String> execute(DataSourceContext context) {
Map<String,String> datasource = new HashMap<String,String>();
datasource.put("value","label"); // make sure you convert numbers to proper strings
return(datasource);
}

The SortedMapDataSource implementation returns the same as a MapDataSource, except Forms Publisher will sort the results alphabetically.

public Map<String, String> execute(DataSourceContext context) {
Map<String, String> datasource = new HashMap<String, String>();
datasource.put("value", "label"); // make sure you convert numbers to proper strings
return (datasource);
}

In order to create a new datasource, we need to edit the local/config/DataSourceConfig.xml file. This file is an XML document which defines which datasources are available to the system as a whole.

For the example that follows, we will create a datasource that will allow a user to select an edition from the current branch. We need to add our datasource to the list by adding the following:

<datasource>
<name>Main Branch Editions</name>
<classname>com.acme.util.BranchEditionsDatasource</classname>
<param name="branch">/default/main</param>
</datasource>

We will build a class with that name as follows:

package com.acme.util;

import com.interwoven.cssdk.factory.CSLocalFactory;
import com.interwoven.cssdk.filesys.CSBranch;
import com.interwoven.cssdk.filesys.CSEdition;
import com.interwoven.cssdk.filesys.CSVPath;
import com.interwoven.datasource.core.DataSourceContext;
import com.interwoven.datasource.SortedValuesMapDataSource;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BranchEditionsDataSource implements SortedValuesMapDataSource {

 public Map<String, String> execute(DataSourceContext context) {
 Map<String, String> parameters = context.getAllParameters();
 Map<String, String> datasource = new HashMap<String, String>();
 String sessionString = context.getSessionId();
 String configFilePath = "/apps/interwoven/teamsite/cssdk/cssdk.cfg";
 Properties localProperties = new Properties();
 localProperties.setProperty("cssdk.cfg.path", configFilePath);
 CSLocalFactory csLocalFactory = (CSLocalFactory) CSLocalFactory.getFactory(localProperties);
 CSClient client = null;

 try {
 client = csLocalFactory.getClient(sessionString, Locale.ENGLISH, "BranchEditionsDataSource", "localhost");
 if (client != null) {
 String branchParameter = parameters.get("branch");
 CSVPath branchVPath = null;
 if (branchParameter != null) {
 branchVPath = new CSVPath(branchParameter);
 } else {
 branchVPath = new CSVPath(context.getServerContext());
 }
 if (branchVPath != null) {
 branchVPath = branchVPath.getBranch(); // make sure we have a branch, really...
 CSBranch branch = client.getBranch(branchVPath, true);
 if (branch != null) {
 CSEdition[] editions = branch.getEditions(true, false, -1);
 for (CSEdition edition : editions) {
 datasource.put(edition.getVPath().toString(), edition.getName() + " - " +                                             edition.getDescription());
 }
 } else {
 throw new NullPointerException("branch is null");
 }
 } else {
 throw new NullPointerException("branch vpath is null");
 }
 } else {
 throw new NullPointerException("client is null");
 }
 } catch (Exception ex) {
 datasource = new HashMap<String, String>();
 datasource.put("error", "An error occured. Please contact your administrator");
 Logger.getLogger(BranchEditionsDataSource.class.getName()).log(Level.SEVERE, null, ex);
 }

 return (datasource);
 }
}

The context gives you all you need to connect to the local Teamsite server using the session id. From there, we can either grab the editions of the branch passed as a parameter or the current branch. You can also extend

com.interwoven.livesite.common.cssdk.datasource.AbstractDataSource.

This will give you the ability to grab the client with a simple method call of:

CSClient client=getClient(context);

You will have to use the parameters that the class expects to make the connection:

<param name="servername">teamsite</param>
<param>http://teamsite:80</param>
<param name="csFactory">com.interwoven.cssdk.factory.CSLocalFactory</param>

Once the class has been compiled into the toolkit, we can then tell out formspublisher item to use it.

we can use a “inline” element, with a Datasource “pseudo-command” (note that this is case sensitive). This works for text instances and select, radio and checkbox instances.

<select>
    <inline command="Datasource:executeComponent:Main Branch Editions" />
</select>

All you now need to do is check the work in the form’s drop down list..

As I have said before, it’s the little things that throw you. It’s how the 80-20 rule got created: you spend 20% of your time developing 80% of the functionality and 80% of your time developing the remaining 20%.

This time, I was attempting to read parameters from a workflow CGI task. Parameters are passed to a workflow task from the command property of the task:

Command: http://$IW_SERVER/iw-cc/urlexternaltask?someparam=somevalue&someotherparam=someothervalue

When reading the map, the parameters are passed as a map of String arrays, not as a map of Strings.