Showing posts with label KnowledgeTree. Show all posts
Showing posts with label KnowledgeTree. Show all posts

Monday, October 20, 2008

KnowledgeTree integration using Apache Camel

What the hump is Apache Camel?

Camel is a Spring-based Integration Framework that implements routing and mediation rules using a Java-based Domain Specific Language (DSL or Fluent API), via a Spring-based XML configuration files or via Scala DSL.

The upcoming version 1.5 release includes a tutorial using Axis 1.4 with Camel. I used this example as a springboard to craft a document publisher to KnowledgeTree document management system via it's Axis-based web services.

The basic document publisher picks up an XML file, uploads it to KnowledgeTree using Apache HttpClient, and invokes KnowledgeTree's add_document webservice to include the file into the document repository. Here is the associated routing rule:

from("file:src/data?delete=true").process(ktDocUploader).to("file://target/test?noop=true");

Once the document-uploader function is implemented, the power and versatility of Camel as a routing and mediation framework comes into play. Camel supports a wide array of components allowing for more points of integration. In the above example, the File endpoint can easily be changed to FTP, SMTP, or JMS messaging without affecting the Processor code.

Route interceptors can be used to insert simple tasks like logging or complex ones like payload inspection for conditional application of logic.

intercept(xpath("/person[@user='rexjun']")).to("log:VIP").proceed();

A delivery failure is handled automatically by the Dead Letter Channel processor. I wanted failed deliveries to be deposited to another folder and this rule was implemented by a single line of code:

errorHandler(deadLetterChannel("file://target/errors"));

Aside from the Dead Letter Channel processor, Camel supports most of the Enterprise Integration Patterns and lends itself as a component of the Apache ServiceMix ESB project.

Sunday, August 3, 2008

ZK Version 3.0.7 released

The ZK front-end app to KnowledgeTree was developed using version 3.0.6. The newer version 3.0.7 was released on August 1 and I had the chance to install ZK from scratch and run the ZK front-end app successfully.

Here are the steps:

  • download zk-bin-3.0.7.zip from sourceforge.net
  • copy the MyApp directory (from the demo package) under the webapps directory of Apache Tomcat. Rename it to accordingly (I used zk307 in this example).
  • Create the WEB-INF/lib sub-directory. Place here all the jars found under the dist/lib directory of the zk-bin-3.0.7 package. Copy the jars under the ext and zkforge directories also but omit the directories - place all jars under WEB-INF/lib
  • Add the ktws.jar and the jars required by Apache Axis (see previous blog entry)

Fire up Tomcat and point your browser to http://localhost:8080/zk307/ktlogin.zul

There are over 9 new features and 22 bug fixes under this new release. Read more about the new ZK release here.

Saturday, August 2, 2008

ZK + SOAP + KnowledgeTree

Here's a working ZK app that performs a SOAP login to KnowledgeTree's web services.



I've uploaded the source code to a KnowledgeTree repository and can be downloaded using the KT-Gadget.

You need to download the following files:
  • ZK front-end app (ktlogin.zul)
  • ZK include script (ktlogin.zk)
You can download the Ajax spinner here or create your own.

My testbed:
  • Apache Tomcat Version 5.5.26
  • ZK 3.0.6
  • Windows XP Prof Service Pack 2
  • Mozilla Firefox 3.0

Sunday, July 20, 2008

Using KnowledgeTree SOAP Services from Java apps

I'm making a front-end to KnowledgeTree using the ZK Ajax framework so I eventually dipped my fingers into KT's web service integration from a Java app. My previous SOAP projects involved a Tcl client and a Google gadget using the javascript/php combo.

KnowledgeTree's wiki article on Web Service integration using Java has been most helpful. Since I'm doing my development work on Windows, I've adapted the procedures slightly. And since ZK uses the Beanshell interpreter to execute the Java code, I also modified the tester client application accordingly.

What's needed:

The proxy classes were generated using the WSDL2Java utility provided by the Apache Axis. I'm providing the packaged ktws.jar available for download here. To generate the proxy classes yourself, you would need the Java Compiler and not just the JRE. Here's the batch file to generate the jar package.

set CP=axis-1_4/lib/axis.jar;axis-1_4/lib/commons-discovery-0.2.jar
set CP=%CP%;axis-1_4/lib/commons-logging-1.0.4.jar;axis-1_4/lib/jaxrpc.jar
set CP=%CP%;axis-1_4/lib/log4j-1.2.8.jar;axis-1_4/lib/saaj.jar
set CP=%CP%;axis-1_4/lib/wsdl4j-1.5.1.jar
java -cp %CP% org.apache.axis.wsdl.WSDL2Java -c T1 -p com.pipoltek.ktws http://docs.pipoltek.com/ktwebservice/webservice.php?wsdl
"%JAVA_HOME%\bin\javac" -d . -classpath %CP% com/pipoltek/ktws/*.java
"%JAVA_HOME%\bin\jar" cvf ktws.jar .\com\pipoltek\ktws\*.class

Here's the Beanshell script to execute a login via SOAP.

/*
filename: ktsoaptest.bsh
to run, type from the command-line:
java -cp bsh-2.0b4.jar bsh.Interpreter ktsoaptest.bsh
*/

// add axis classes
addClassPath ("./axis-1_4/lib/axis.jar");
addClassPath ("./axis-1_4/lib/axis-ant.jar");
addClassPath ("./axis-1_4/lib/jaxrpc.jar");
addClassPath ("./axis-1_4/lib/commons-logging-1.0.4.jar");
addClassPath ("./axis-1_4/lib/commons-discovery-0.2.jar");
addClassPath ("./axis-1_4/lib/log4j-1.2.8.jar");
addClassPath ("./axis-1_4/lib/wsdl4j-1.5.1.jar");
addClassPath ("./axis-1_4/lib/saaj.jar");

// knowledgetree proxy classes
addClassPath ("./ktws.jar");

// others
addClassPath ("./lib/activation.jar");
addClassPath ("./lib/mail.jar");

import com.pipoltek.ktws.KnowledgeTreeBindingStub;
import com.pipoltek.ktws.Kt_response;
import com.pipoltek.ktws.Kt_folder_contents;
import com.pipoltek.ktws.Kt_workflow_transitions_response;
import org.apache.axis.client.Service;
import java.net.URL;
import com.pipoltek.ktws.Kt_folder_item;

URL url = new URL("http://kt352c.pipoltek.com/ktwebservice/webservice.php");

KnowledgeTreeBindingStub stub = new KnowledgeTreeBindingStub(url, new Service());

System.out.println("Logging into KnowledgeTree");
Kt_response response = stub.login("rexjun","cabanilla","127.0.0.1");

int status_code=response.getStatus_code();
String session;
if (status_code == 0) {
session = response.getMessage();
System.out.println("Session: " + session);
} else {
System.out.println("Status Code: " + status_code);
System.out.println("Session: " + response.getMessage());
return;
}

I'll be using this piece of code to invoke SOAP services from my ZK app. More on this later..

Friday, June 20, 2008

KnowledgeTree document download via Google Gadget

This is an installment feature for the KT-Gadget, a widget for KnowledgeTree, an open-source document management system.

As mentioned in the previous post, I employed an application proxy to act as a bridge between a Google Gadget and KnowledgeTree's SOAP services. Originally, the application proxy responses were formatted in XML. Moving forward, I decided to adopt the JSON format instead of XML owing to simpler methods in handling JSON feeds from both PHP and (of course) Javascript worlds.

The application proxy's SOAP client is based on PHP and converting an associative array of response items to JSON format is implemented simply by using PHP's json_encode function. At the gadget side, the requests for remote data are invoked using Google's _IG_FetchContent function. Using Javascript's eval() function, the returned JSON text response is converted to an object whose members can be easily accessed using dot or subscript operators.

Here are the details.. To download a document, KT-Gadget sends a request to the helper application by invoking the url:

http://kt-gadget.pipoltek.com/pipoltek.com/json/?oid=download_document&sid=rsop4e364ptf4slkp2a2vtvb96&did=4

The JSON response by the application proxy is of the form:
{"response":{"status_code":0,"message":"http:\/\/docs.pipoltek.com\/ktwebservice\/download.php?code=d2db2c0868ab0c9cd053fe2e43ab9926d1bcf94c&d=4&u=6a0j9qqpqc11de6nju6o0ej5o7"}}

The relevant block of KT-Gadget code is reproduced hereunder:

var url = "http://kt-gadget.pipoltek.com/pipoltek.com/json/?oid=get_folder_contents&sid=" + sessionID__MODULE_ID__ + "&fid=6";
_IG_FetchContent(url, function (jsonFeed) {
var jsonData = eval('(' + jsonFeed + ')');
if (jsonData.response.status_code != 0) {
alert ('Error in retrieving document!');
} else{
dd_link = jsonData.response.message;
_gel("ktg-lnk").innerHTML = '<a href="' + dd_link + '">@';
}
})

Once the application proxy has responded, the JSON text response (jsonFeed) is converted to an object (jsonData) using the eval() function. The response contains the download URL the value of which can be referenced as jsonData.response.message.

Where did the Session-ID (sid) and Document-ID (did) used as URL parameters come from? The Session-ID is assigned during login and the Document-ID, together with the rest of the document details, is returned by a call to get_folder_contents.

Here's the login invocation:
http://kt-gadget.pipoltek.com/pipoltek.com/json/?oid=login&uid=rexjun&pwd=cabanilla

The invocation to get_folder_contents:
http://kt-gadget.pipoltek.com/pipoltek.com/json/?oid=get_folder_contents&sid=0j8jkhjn55t7r3b0rshbcchpn1&fid=6

If you get a "Session is invalid" error, it's because the link is using an ID of an expired session. Change the URL parameter (sid) to a fresh Session-ID returned by the login call to get better results.

You may try out KT-Gadget by adding it to your iGoogle page or view the gadget source code here.

Thursday, June 12, 2008

Accessing Soap Services from Google Gadgets

The earlier gadget projects I developed have utilized third-party web services from a variety of sources:

  • Google Maps for geographic data
  • Google Docs spreadsheet as XML datasource
  • IP-Geolocation (IP address->Country) service from HostIP.Info
  • User profile and gaming statistics as XML feeds from Xfire.
Given the constraints of a space-restricted, remotely-hosted, javascript-only application, gadgets primarily rely on web services to provide richer data content without the attendant complexity. However, javascript is beset by cross-domain restrictions that even impact xmlHttpRequest, the very underpinnings of Ajax. Since Google gadgets are javascript apps, the same-origin security policy prevents it from making requests outside the domain it is running on.

Although Google addressed this problem by providing the _IG_FetchXMLContent function, it lacks support for https, SOAP and POST. It looks like the newer gadget.io.makeRequest function is intended to address these deficiencies. However, the availability of the gadget.io.* functions in Google's production servers is yet forthcoming.

I decided not to hold my breath..

KT-Gadget is a widget front-end to KnowledgeTree - an open-source document management system. To access KnowledgeTree's SOAP services, I had to deploy another application to receive the requests from Google's _IG_FetchXMLContent calls, make the corresponding SOAP requests, and generate the appropriate XML output. Two basic functions are implemented: login and get_folder_contents. The functions are based on PHP, the same language that was used to develop KnowlegeTree.

After pushing the login button, the gadget will indicate the progress of the subsequent operations:
  • Logging in
  • Fetching Documents
  • Sorting Documents
In its current state, the gadget is useful as a watcher of some sort of inbox for workflow-enabled documents like incoming faxes needing disposition or requisitions requiring approval. Sit around and watch your work pile up...

I plan to implement two more functions: download_document and perform_document_workflow_transition. We don't want work to pile up, do we? Other nice-to-have features: watch all accessible folders and sub-folders; show number of documents per folder; folder and document colors based on certain thresholds like document count, age, metadata contents (priority, etc); automatic refresh.

But don't hold your breath...

Tuesday, April 22, 2008

OpenID login for Knowledgetree

I have replaced (not integrated) KnowledgeTree authentication with OpenID. Supporting both default and OpenID authentication requires more work and that can come later.

This is by no means final since only limited testing has been performed so far. I've decided to post this in its raw form so I can solicit comments and suggestions from the community. And besides, my blog has not been updated for awhile now..

I'll detail the procedures here. I took notes but I may have missed something. Diligence is not one of my virtues..

  • Copy login.php to login-orig.php (save original, let's modify login.php)
  • Change template ktcore/login -> ktcore/openid
  • Copy ./templates/ktcore/login.smarty to openid.smarty
    - change invocation of stylesheet kt-login.css -> kt-openid.css
    - change the Username prompt label to OpenID
    - add class="openid" to input tag for username
    - change name="username" to name="openid_url"
    - remove the password input field

  • Download the OpenID logo
  • Upload logo as openid-bg.png to ./resources/graphics
  • Copy ./resources/css/kt-login.css to kt-openid.css
  • Add input.openid to kt-openid.css, as follows:
    input.openid {
    border: 1px solid #666;
    width: 232px;
    background: url(../graphics/openid-bg.png) no-repeat;
    padding-left: 18px;
    }
Note the OpenID logo in the input field. This is implemented by the input.openid section of the stylesheet. I also changed the input field name to openid_url in conformance to OpenID specifications. I'm using Verisign PIP as OpenID Identity Provider and the Seatbelt browser plug-in kicks in only when openid_url is used as identifier.

I installed version 2.x.x of the PHP OpenID Library, as follows:
  • Create the directory ./thirdparty/OpenID
  • Copy the file common.php found in the examples directory. Also copy the Auth directory from the library.
  • Add the following functions to common.php
    function fixslashes($s) {
    return get_magic_quotes_gpc() ? stripslashes($s) : $s;
    }

    function normOpenIDUrl($oid_url) {
    $claimed_id = strtolower (fixslashes($oid_url));
    $has_scheme = preg_match ('#^https\://#', $claimed_id) === 1;
    $has_scheme = $has_scheme || preg_match ('#^http\://#', $claimed_id) === 1;
    $has_tslash = preg_match ('#/$#', $claimed_id) === 1;
    return (($has_scheme?'':'http://') . $claimed_id . ($has_tslash?'':'/'));
    }
  • Overwrite the original functions in common.php with these
    function getReturnTo() {
    return sprintf("%s://%s:%s/login.php",
    getScheme(), $_SERVER['SERVER_NAME'],
    $_SERVER['SERVER_PORT']);
    }

    function getTrustRoot() {
    return sprintf("%s://%s:%s/",
    getScheme(), $_SERVER['SERVER_NAME'],
    $_SERVER['SERVER_PORT']);
    }
  • Change config/dmsDefaults.php to include the library in the path..
    $KTInit->prependPath(KT_DIR . '/thirdparty/OpenID');
The rest of the changes are in login.php, the source of which can be found here.

I've disabled automatic sign-up so make sure to create an OpenID account with Administrator privileges before you apply the changes. To prevent duplicate accounts, the usernames are OpenID URLs in normalized form (with leading http:// and trailing /). Thus, the username http://pipoltek.blogspot.com/ applies to any of the following acceptable OpenID identity URLs:
  • pipoltek.blogspot.com
  • http://pipoltek.blogspot.com
  • http://pipoltek.blogspot.com/
I've successfully authenticated using my OpenID accounts from Blogspot, Technorati and Verisign PIP. However, I had login failures using my Yahoo OpenID and a delegate identity URL.

My testbed:
  • VMWare Server version 1.0.4.56528
  • KnowledgeTree OSS 3.4.2 VM Appliance from rPath
  • Microsoft Windows XP Home Service Pack 2
  • Mozilla Firefox 2.0.0.14

Monday, January 21, 2008

A Tcl Web Service client for KnowledgeTree Document Management System

Sometime last year, I dabbled with KnowledgeTree's web services and (somehow I've forgotten why) I ended up with a Tcl client to perform a document upload.

Before I lose this chunk of code or forget about it altogether (and it happens a lot), I decided to post it here:


package require SOAP
package require http
package require TclCurl

set prx "http://192.168.211.128/ktwebservice/webservice.php"
set url "http://192.168.211.128/ktwebservice/upload.php"
set usr "admin"
set pas "admin"
set ipa "any"

set upf "upload-me.txt"
set typ "multipart/form-data"

proc log_it { chn msg } {
set stm [clock format [clock seconds] -format "%Y/%m/%d %H:%M:%S %a"]
puts $chn "$stm : $msg"
}

SOAP::create kt_login \
-proxy $prx \
-params { "username" "string" "password" "string" "ip" "string"} \
-name login
SOAP::create kt_add_document \
-proxy $prx \
-params { "session_id" "string" "folder_id" "int" "title" "string" "filename" "string" \
"documentype" "string" "tempfilename" "string" } \
-name add_document
SOAP::create kt_logout \
-proxy $prx \
-params { "session_id" "string"} \
-name logout

set log "debug.log"

if { [catch {open $log a} out] } {
puts stderr "Error: $out"
set out "stderr"
}

set rsp [kt_login $usr $pas $ipa]
set sta [lindex $rsp 1]
set ses [lindex $rsp 3]
log_it $out "Login: status ($sta); session ($ses)"

set opt "session_id $ses action A"
set crl [curl::init]
set rsp [$crl configure -url $url -bodyvar rsp -post 1 \
-httppost [list name "file1" file $upf contenttype $typ] \
-httppost [list name "session_id" contents $ses] \
-httppost [list name "action" contents A] \
-httppost [list name "output" contents php] \
]
catch { $crl perform } curlErrorNumber
if { $curlErrorNumber != 0 } {
error [curl::easystrerror $curlErrorNumber]
}
$crl cleanup

log_it $out "Upload: response ($rsp)"

set ps1 [string first "\"tmp_name\";s:" $rsp 0]
set ps2 [string first ":\"" $rsp [expr $ps1 + 12]]
set ps3 [string first "\";s:5:\"error\"" $rsp 0]
set tmp [string range $rsp [expr $ps2 + 2] [expr $ps3 - 1]]

# Define folder where to upload files
set fld 1;
set ttl "My Document";
set doc "Default"

set rsp [kt_add_document $ses $fld $ttl $upf $doc $tmp]
log_it $out "Add Document: response ($rsp)"

set rsp [kt_logout $ses]
log_it $out "Logout: response ($rsp)"

close $out

The upload facility is implemented by upload.php and returns a response coming out of php's serialize() function. I've mangled upload.php to return a response in xml or json format. The code changes are posted in the Knowledgetree community forum here.

The above Tcl code digests the php's serialize() output.

For xml output, here are the relevant changes:

set crl [curl::init]
set rsp [$crl configure -url $url -bodyvar xml -post 1 \
-httppost [list name "file1" file $upf contenttype $typ] \
-httppost [list name "session_id" contents $ses] \
-httppost [list name "action" contents A] \
-httppost [list name "output" contents xml] \
]
...
set top [dom parse $xml]
set sel [$top selectNodes /results/uploads/document/tmp_name/text()]
set tmp [$sel nodeValue]
$top delete

And for json..

set rsp [$crl configure -url $url -bodyvar rsp -post 1 \
-httppost [list name "file1" file $upf contenttype $typ] \
-httppost [list name "session_id" contents $ses] \
-httppost [list name "action" contents A] \
-httppost [list name "output" contents json] \
]
...
set ps1 [string first "\"tmp_name\":\"" $rsp 0]
set ps2 [string first "\",\"error\"" $rsp 0]
set tmp [string range $rsp [expr $ps1 + 12] [expr $ps2 - 1]]

Check the contents of debug.log for any errors. A successful operation produces this output: