Tuesday, August 26, 2008

Drools Rule Templates

Drools 5 sees the addition of a new feature - rule templates. Rule templates allow you to set up templates (!) that may then be merged with data. You can think of them as similar to decision tables but far more powerful. With Rule Templates the data is separated from the rule and there are no restrictions on which part of the rule is data-driven. So whilst you can do everything you could do in decision tables you can also do the following:
  • store your data in a database (or any other format)
  • conditionally generate rules based on the values in the data
  • use data for any part of your rules (e.g. condition operator, class name, property name)
  • run different templates over the same data
I find the best way to explain things is via example so over a few posts I will take you through three of the examples in the drools-examples project. The first shows how to achieve exactly the same functionality as a decision table using a rule template. The second example extends the first to include array fields and optional fields. The third gives an example of using a rule template with a different datasource - in this case Plain Old Java Objects (POJOs), and conditional templates.

The first example is SimpleRuleTemplateExample. We start with two files - the data and the template. In this case the data is the spreadsheet from an example you may be familiar with.

ExampleCheese.xls


If this was a regular decision table there would be hidden rows before row 1 and between rows 1 and 2 containing rule metadata. With rule templates the data is completely separate from the rules. This has two handy consequences - you can apply multiple rule templates to the same data and your data is not tied to your rules at all. So what does the template look like?

Cheese.drt
1  template header
2 age
3 type
4 log
5
6 package org.drools.examples.templates;
7
8 global java.util.List list;
9
10 template "cheesefans"
11
12 rule "Cheese fans_@{row.rowNumber}"
13 when
14 Person(age == @{age})
15 Cheese(type == "@{type}")
16 then
17 list.add("@{log}");
18 end
19
20 end template
Line 1: all rule templates start with "template header"
Lines 2-4: following the header is the list of columns in the order they appear in the data. In this case we are calling the first column "age", the second "type" and the third "log".
Lines 5: empty line signifying the end of the column definitions
Lines 6-9: standard rule header text. This is standard rule DRL and will appear at the top of the generated DRL. Put the package statement and any imports and global definitions
Line 10: The "template" keyword signals the start of a rule template. There can be more than one template in a template file. The template should have a unique name.
Lines 11-18: The rule template - see below
Line 20: "end template" signifies the end of the template.

The rule templates rely on MVEL to do substitution using the syntax @{token_name}. There is currently one built-in expression, @{row.rowNumber} which gives a unique number for each row of data and enables you to generate unique rule names. For each row of data a rule will be generated with the values in the data substituted for the tokens in the template. With the example data above the following rule file would be generated.

package org.drools.examples.templates;

global java.util.List list;

rule "Cheese fans_1"
when
Person(age == 42)
Cheese(type == "stilton")
then
list.add("Old man stilton");
end

rule "Cheese fans_2"
when
Person(age == 21)
Cheese(type == "cheddar")
then
list.add("Young man cheddar");
end
The code to run this is simple

//first we compile the spreadsheet with the template
//to create a whole lot of rules.
final ExternalSpreadsheetCompiler converter = new ExternalSpreadsheetCompiler();
//the data we are interested in starts at row 2, column 2 (e.g. B2)
final String drl = converter.compile(getSpreadsheetStream(), getRulesStream(), 2, 2);
We create an ExternalSpreadsheetCompiler object and use it to merge the spreadsheet with the rules. The two integer parameters indicate the column and row where the data actually starts - in our case column 2, row 2 (i.e. B2)

Friday, August 22, 2008

Drools execution server - for the early adopters

This is a very early announcement of the first drop of the drools-server module. This is a NEW module which allows execution of stateless rules via a REST-style web service interface (XML and JSON). (Watch this space for stateful). This means you can deploy rules and excute them from any sort of client (no libraries needed - not even a JVM).

This builds on the rule agent itself to managing what packages are used, and can work with guvnor or against any rule source (including drl files, which I used for testing).

drools-server is a war file, which can be deployed in a servlet container. RuleBase configurations are then accessed via URL: http://server/drools-server/knowledgebase/{ruleagentConfigName}.

The data you post is either XML or JSON, and maps to the fact model that the rules use - there are globals, input facts, and output facts (in the case of globals and output facts, the data is returned in the response message - which is how you get results back !).

When drools-server.war is deployed, going to /drools-server should show a page like:


Which has some example request/response and details.

Benefits of a simple rest approach:
No libraries ! Just use HTTP and XML or JSON on the "client" (many languages have that built in).

In fact, to test this out I did a simple rails app and client using nothing but its plain old http library, and the "json" library (JSON == Javascript Object Notation - a fast textual object graph serialization format - it is literal Javascript syntax for Arrays and Maps). (I would like to do a PHP and .Net one next - time permitting - after my holidays !).

You can see from the above the JSON strucutre created in ruby literals, and then converted to JSON format at the last minute (which looks pretty similar). To parse the response:
answer = JSON.parse(data)
#digging out the results:
puts answer["knowledgebase-response"]["globals"]["named-fact"]["fact"]["teamName"]
#if there is more then one fact, they are a list
puts answer["knowledgebase-response"]["inOutFacts"]["named-fact"][0]["fact"]["specialty"]

You can see there is some hierarchy to the results and the request - which is easy to see with JSON. This example "client" code is in drools-server/sample-clients/ruby_json.rb.

Enjoy !

Thursday, August 21, 2008

Product update: Guvnor security



A common need and desire of the web interface of Guvnor is to be able to have users of different technical abilities interact with it. Another need is to be able to allocate people different sets of data to "own".

These have now been addressed in Guvnor (these features should be available in Milestone 2 and onwards).


Managing users:

Typically users identities are managed in a centralised directory - application servers can integrate with these directories (eg active directory, LDAP) so users to guvnor can be authenticated without having to create another duplicate identity. It is also possible (thanks to JAAS) to define what users have the "admin" role for Guvnor (note that an Admin user of Guvnor doesn't have to really be a system administrator). Further to this, guvnor augments this identity with data specific permissions, which are managed in Guvnor itself.



Roles and Permissions:
There are really 2 system wide roles: Users who are Administrators and users who are not. Easy ! Administrators can see and do anything. Out of the box, the permission system is turned off, and every user is an administrator (this is pretty much how things used to work). There is also a system setting in components.xml that can turn the permissions system on and off (so people can manually override if needs be). A administrator can also give other users admin rights, regardless of their roles in the external directory service.




There are several types of permissions:

Per package:
Package Administrator ("owns" a package - can deploy etc, but has no administrative rights to the system). Package developer - this permissions allows users to create new items, edit etc - but only at the package level (not deploy). They can also run and create tests. Package readonly - well this one is pretty obvious.

Per Category:
This is the "interesting" one - as assets (rules) can be tagged with multiple categories, you can use these to assign permissions to an "analyst" type of user. A user can be assigned multiple categories. A user can then edit and view any asset that is tagged in that category (regardless of package). A user that only has category permissions will not be shown any package views or details, and will only see the simple categories view. This allows administrators and managers to control exactly what these users can and can't see.



It is the per category "analyse" permission which is quite useful - you can also augment their permissions with a specific package (so on top of their category rights, they can see and play with a particular package - which may be used as a "practice" area, or test area for instance).

The above provides a few ways to manage permissions in a coarse or fine grained way, as suits the different types of users.

Wednesday, August 20, 2008

New DroolsTab geo-information tool release

DroolsTab is a tab plug-in to the open source ontology editor Protege (http://protege.stanford.edu). Version18.08.2008 has just been released. It uses the open source geo-information system Java library OpenMap (http://openmap.bbn.com) and the open source Java RETE rule engine Drools (http://labs.jboss.com/drools/). It can be used for visual authoring of the complex spatial process simulation scenarios and general rule base authoring. It includes several demo examples of spatial simulation in the sea, air and ground environments.

New features:
  • parallel-consecutive "tasks" instead of only consecutive "phases",
  • "signal transmission" between scenarios and/or rule sets,
  • using Protege server for remote signal transmission,
  • better documentation,- scenario development support.
New examples:
  • "Terrorist Location Distribution",
  • "Road Traffic",
  • "Remote Police Work" (using Protege server),
  • spatial scenario simulation server.

More declarative workflow

Some process languages allow you to basically do anything you want by allowing you to include code fragments in your process that are then executed during the execution of that process. This allows you for example to interact with basically any external service by writing a piece of code that invokes the service. While this approach offers a huge amount of power and flexibility to the end user, there are some mayor problems as well:
  • Using code fragments to specify work is not really declarative: it does not only specify what should be executed but also how.
  • By embedding the code in your process your are also usually linking your process to one specific environment. For example, creating a new personnel record is usually done differently in different locations / companies.
  • If code fragments are used to communicate with external services, the process has no way of knowing this, which might result in errors in later stages. For example, a piece of code might be used to request the asynchronous execution of an external service, but the process might have been completed or aborted by the time the results return.
  • It is usually difficult to test your process in a test environment without having to change the code (or making it a lot more complex), as the code usually refers to specific services and you don't really want to contact production services during testing.
While it also possible to embed code fragments directly into your processes in Drools Flow, we encourage users to use work items to communicate with external services. A work item represents an abstract unit of work that needs to be executed by an external service. A work item specifies a name (which uniquely identifies the type of work, e.g. email or log) and parameters (as name-value pairs). These parameters can be hardcoded when defining the process or can be dynamically derived from the runtime process state.

Work item handlers are used to link abstract work item to the actual implementation that knows how to contact the specific external services. They must implement the executeWorkItem(WorkItem, WorkItemManager) method and signal the WorkItemManager upon completion (or abortion) of the work item, including possible results (as name-value pairs). These results can then of course be used in the process instance. Work item handlers need to be registered (at deployment or even at runtime) at the process engine. This allows us to for example to register a custom work item handler in test cases to simulate a specific environment.

The use of work items allows users to easily extend the power of the process engine themselves, by creating domain-specific work item nodes. The process designer supports the definition of different types of work items using a configuration file that defines the name, the parameters, the results and possibly a custom icon for the work item.

We have already created a small set of work items for common problems that can be used out of the box:
  • Sending email
  • Logging messages
  • Finding files on a file system
  • Archiving files
  • Executing system commands
The following screenshot shows an example where a Finder is used to select all files on the file system that match certain criteria, followed by a logger that logs and checks each of these files, where the valid files are archived and then copied to a specific location, where an email is sent to the administrator after successful completion of this process.


We are working on adding more work items by default, like invoking a web service, integration with services on the JBoss ESB, etc. With the help of the community, we hope to extend this set of work items significantly so we can offer users a large selection of work items in different application domains.

Sunday, August 17, 2008

Drools Solver article on DZone's Javalobby

I've posted an overview article about Drools Solver on DZone's Javalobby:
Solving planning problems: Introducing Drools Solver

Saturday, August 16, 2008

Tuning Guvnor

Guvnor is the business rules management system in Drools 5. When you deploy it out of the box, you get an unsecured web application that stores data in Jackrabbit’s embedded Derby database.

This article explains how to tune Guvnor deployed on JBoss Application Server 4.2.3. This means that we will use the container’s configuration files and security infrastructure. We will cover enabling password validation based on an LDAP server, moving from the default data repository, and enabling SSL for better security.
  1. Installation
  2. Quick introduction
  3. Enable user/password validation
  4. Use OpenLDAP as a user repository
  5. Use MySQL as a data repository
  6. Enable SSL
  7. How to use a secured Guvnor package
  8. Summary
  1. Installation

  2. We will deploy Guvnor as an exploded archive on the JBoss Application Server. Download JBoss AS 4.2.3.GA and extract it to /data/jboss-4.2.3.GA. Download Guvnor M1 and extract it to the deploy directory under /data/jboss-4.2.3.GA/server/<chosen_config>/deploy/drools-guvnor.war. From now on I'll use $JBOSS_SERVER as /data/jboss-4.2.3.GA/server/<chosen_config> and $GUVNOR as /data/jboss-4.2.3.GA/server/<chosen_config>/deploy/drools-guvnor.war. To verify a successful deployment, start the server:
    $ /data/jboss-4.2.3.GA/bin/run.sh -c <chosen_config>

    =========================================================================

    JBoss Bootstrap Environment

    JBOSS_HOME: /data/jboss-4.2.3.GA

    JAVA: /usr/local/jdk1.5.0_11/bin/java

    JAVA_OPTS: -Dprogram.name=run.sh -server -Xms128m -Xmx512m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Djava.net.preferIPv4Stack=true

    CLASSPATH: /data/jboss-4.2.3.GA/bin/run.jar:/usr/local/jdk1.5.0_11/lib/tools.jar

    =========================================================================

    17:13:53,732 INFO [Server] Starting JBoss (MX MicroKernel)...
    17:13:53,734 INFO [Server] Release ID: JBoss [Trinity] 4.2.3.GA (build: SVNTag=JBoss_4_2_3_GA date=200807181417)
    17:13:53,736 INFO [Server] Home Dir: /data/jboss-4.2.3.GA
    .
    .
    .
    17:14:38,366 INFO [TomcatDeployer] deploy, ctxPath=/drools-guvnor, warUrl=.../deploy/drools-guvnor.war/.
    .
    .
    Navigate to http://localhost:8080/drools-guvnor and login as 'admin' without any password.


    Guvnor is now up and running. Let’s see how we can get started quickly.

  3. Quick introduction
    Before you can start using Guvnor for rule authoring, you need to perform some basic setup. We will need to create a category, make a package, and upload your facts. Let’s log in.

    • Categories

    • You will need to create at least one category, under which you will store your rules. Categories are for classification purposes. You will normally want to provide meaningful names like ‘Insurance’ or ‘Discount,’ but for this test instance, we’ll just create ‘MyNewCategory.’ On the left side click on the ‘Administration’ tab, expand the ‘Admin’ list, and then click on ‘Categories’:


      Create a category by clicking on ‘New category’ and providing a name.

    • Packages

    • A package is a place where rules are stored. It also includes globals and imports of all the facts and other classes we would like to use in our rules–for example, ArrayLists or Iterators. To create a package, expand the ‘Package’ tab and click on ‘Create New’ -> ‘New Package’


      You can create a new package by providing its name (our example is myNewPackage) or by importing one from a drl file. In either case, you will need to provide the facts you’re going to use in your rules.

    • Facts

    • Next, you will create classes you would like to use in your rules. For this example, I’ll use a Driver and a Car class:
      Driver.java
      package kijanowski.eu;

      public class Driver {

      private String name;
      private int age;
      private Car car;

      public Driver() {}

      public Driver(String name, int age, Car car) {
      this.name = name;
      this.age = age;
      this.car = car;
      }

      public String getName() {
      return name;
      }

      public void setName(String name) {
      this.name = name;
      }

      public int getAge() {
      return age;
      }

      public void setAge(int age) {
      this.age = age;
      }

      public Car getCar() {
      return car;
      }

      public void setCar(Car car) {
      this.car = car;
      }

      }

      Car.java
      package kijanowski.eu;

      public class Car {

      private String color;
      private double value;

      public Car() {}

      public Car(String color, double value) {
      this.color = color;
      this.value = value;
      }

      public String getColor() {
      return color;
      }

      public void setColor(String color) {
      this.color = color;
      }

      public double getValue() {
      return value;
      }

      public void setValue(double value) {
      this.value = value;
      }

      }

      Compile these java files (if not already done by your IDE) and create an java archive:
      $ javac -d . *.java
      $ jar cf model.jar kijanowski
      Import the new model to your package. From the ‘Packages’ tab click on ‘Create New’ -> ‘Upload new Model jar’:


      Provide a name and select myNewPackage as the destination package. Provide a path (or click on Browse and navigate) to your facts archive. Finally, click on Upload.
      When you choose myNewPackage from the ‘Packages’ tab, you should see the imported facts:


      Before these facts are available in rules, you need to save this package. Click on ‘Save and validate configuration’.

    • Rules

    • Now you can create rules. From the ‘Package’ tab select ‘Create New’ -> ‘New Rule’. Provide a name, choose a category, and select your favorite rule format with myNewPakage as the destination package:


      A simple example is shown below:


      Validate and save your rule by first choosing ‘Validate’ and–if all is ok–then ‘Save changes’. Now you can make this package and all its rules available to your applications.

      Choose your package and click on ‘Save and validate configuration.’ Before building it, click on ‘Show package source’ to have a look at the whole package. When you’re done looking at the source, choose ‘Build package’. You should be able to access the package under http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST and in drl format at http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST.drl.
      Now let’s create a simple Drools application that will use a package served by Guvnor.

      Open Eclipse and create a new Drools project (as described in Introduction into Rule Engines) or use your favorite IDE (don’t forget to add drools-core.jar and mvel.jar to your classpath). Add model.jar to your classpath and create a test class:
      package kijanowski.eu;

      import org.drools.RuleBase;
      import org.drools.WorkingMemory;
      import org.drools.agent.RuleAgent;
      import java.util.Iterator;

      public class GuvnorTest {

      public static final void main(String[] args) {

      RuleAgent agent = RuleAgent.newRuleAgent("/Guvnor.properties");
      RuleBase ruleBase = agent.getRuleBase();

      WorkingMemory workingMemory = ruleBase.newStatefulSession();

      Driver d = new Driver("Jarek", 20, null);
      workingMemory.insert(d);

      workingMemory.fireAllRules();

      for (Iterator i = workingMemory.iterateObjects(); i.hasNext();) {
      System.out.println(i.next().getClass().getCanonicalName());
      }
      }

      }
      This time we don’t read a package from the filesystem, but are configuring our rule agent with a properties file. One fact is inserted, rules are fired, and, in the end, we iterate over all facts in the working memory to make sure a Car fact has been inserted. We expect this will happen, don’t we? Check the rule if you’re in doubt. Let’s have a look at the properties file:
      url=http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST
      We provide the url that points to our package. There are a lot more attributes you can provide (read the Guvnor docs for details). When the application is run, you should get following output:
      RuleAgent(default) INFO (Wed Jul 23 20:31:28 CEST 2008): Configuring with newInstance=false, secondsToRefresh=-1
      RuleAgent(default) INFO (Wed Jul 23 20:31:28 CEST 2008): Configuring package provider : URLScanner monitoring URLs: http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST
      RuleAgent(default) INFO (Wed Jul 23 20:31:29 CEST 2008): Applying changes to the rulebase.
      RuleAgent(default) INFO (Wed Jul 23 20:31:29 CEST 2008): Adding package called myNewPackage
      kijanowski.eu.Driver
      kijanowski.eu.Car
  4. This was just a quick introduction into Guvnor. A much more exhaustive description can be found in the Guvnor documentation.

  5. Enable user/password validation

  6. When deploying Guvnor, everyone can access it using the admin username–a password isn’t verified. However, Guvnor is designed to allow access to different users, who may have different skills and rights. Controlling access may be critical. To enable username/password validation, we need to edit Guvnor’s security configuration. This is located in:
    $GUVNOR/WEB-INF/components.xml
    We will want to set JAAS as the new authorization and authentication service. Comment out:
    <security:identity authenticate-method="#{defaultAuthenticator.authenticate}"/ >
    and add:
    <security:identity authenticate-method="#{Authenticator.authenticate}" jaas-config-name="guvnor" />
    That’s all from the apps side. To configure JBoss AS, add the following to $JBOSS_SERVER/conf/login-config.xml:
        <application-policy name = "guvnor">
    <authentication>
    <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag = "required">
    <module-option name="usersProperties">props/guvnor-users.properties</module-option>
    <module-option name="rolesProperties">props/guvnor-roles.properties</module-option>
    </login-module>
    </authentication>
    </application-policy>
    We have chosen the file-based login module. We now need to create two files, where we will provide the admin username, password, and role:
    $JBOSS_SERVER/conf/props/guvnor-users.properties
    admin=admin123
    $JBOSS_SERVER/conf/props/guvnor-roles.properties
    admin=admin
    We have now created an ‘admin’ user with the password ‘admin123′ and its role is ‘admin’.
    You may want to have all your users in a database or directory. For all available login modules, have a look at this wiki.

    In the current Drools 5M1 release, only the admin role is supported. You may want to have a look at drools-guvnor/src/main/java/org/drools/guvnor/server/security/RoleTypes.java for other roles implemented in future releases. Currently it looks like roles provided by JAAS login modules are ignored and have to be set inside Guvnor in the Admin section (not available in M1 release). However this is under development and may change.

  7. Use OpenLDAP as a user repository

  8. There are several reasons why you would want to use an LDAP directory instead of a clear text file - security, provisioning, and reuseability are just a few. First of all, we need a directory. OpenLDAP will do for this example. Download and extract the bits from the OpenLDAP home page. In this example, I've used openldap-2.3.39.tgz. Next, go to the directory where you've extracted the installation files and perform following steps:
    $ mkdir -p /data/openldap-2.3.39
    $ ./configure --prefix=/data/openldap-2.3.39
    $ make depend
    $ make
    $ make install
    For more detailed instructions, look at the INSTALL file or the OpenLDAP Administrator's Guide.
    The next instructions will configure our directory and create a tree which looks like this:


    We need to initialize the LDAP server and provide data like the root suffix, directory manager, and password. We also want to enable SSL like so:
    $ mkdir /data/openldap-2.3.39/ssl
    $ openssl req -newkey rsa:1024 -x509 -nodes -out /data/openldap-2.3.39/ssl/server.pem -keyout /data/openldap-2.3.39/ssl/server.pem -days 365
    Generating a 1024 bit RSA private key
    ....++++++
    ................++++++
    writing new private key to 'server.pem'
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [GB]:EU
    State or Province Name (full name) [Berkshire]:Mazovia
    Locality Name (eg, city) [Newbury]:Warsaw
    Organization Name (eg, company) [My Company Ltd]:Kijanowski
    Organizational Unit Name (eg, section) []:Guvnor
    Common Name (eg, your name or your server's hostname) []:localhost
    Email Address []:a@a.a
    Since this is a self-signed certificate we will need to add it to the client's (JBoss AS) truststore.
    $ openssl
    OpenSSL> x509 -inform PEM -outform DER -in /data/openldap-2.3.39/ssl/server.pem -out /data/openldap-2.3.39/ssl/server.der
    OpenSSL> exit

    $ keytool -import -file /data/openldap-2.3.39/ssl/server.der -keystore $JBOSS_SERVER/conf/ldap.truststore

    Enter keystore password: qwerty
    Owner: EMAILADDRESS=a@a.a, CN=localhost, OU=Guvnor, O=Kijanowski, L=Warsaw, ST=Mazovia, C=EU
    Issuer: EMAILADDRESS=a@a.a, CN=localhost, OU=Guvnor, O=Kijanowski, L=Warsaw, ST=Mazovia, C=EU
    Serial number: d8537a079c5eed59
    Valid from: Wed Jul 16 18:35:50 CEST 2008 until: Thu Jul 16 18:35:50 CEST 2009
    Certificate fingerprints:
    MD5: 25:C5:88:7B:D4:88:02:46:F1:EF:0D:6B:D6:EE:1F:A7
    SHA1: 57:B8:F4:25:77:F0:12:BD:B2:2E:DD:7D:CE:09:D2:D4:96:56:BC:26
    Trust this certificate? [no]: yes
    Certificate was added to keystore
    To enable the new truststore, edit $JBOSS_SERVER/deploy/properties-service.xml and add following lines:
    <attribute name="Properties">
    javax.net.ssl.trustStore=/data/jboss-4.2.3.GA/server/<chosen_config>/conf/ldap.truststore
    javax.net.ssl.trustStorePassword=qwerty
    </attribute>
    Now edit the file /data/openldap-2.3.39/etc/openldap/slapd.conf:
    include         /data/openldap-2.3.39/etc/openldap/schema/core.schema
    include /data/openldap-2.3.39/etc/openldap/schema/cosine.schema
    include /data/openldap-2.3.39/etc/openldap/schema/inetorgperson.schema

    pidfile /data/openldap-2.3.39/var/run/slapd.pid
    argsfile /data/openldap-2.3.39/var/run/slapd.args

    database bdb
    suffix "dc=kijanowski,dc=eu"
    rootdn "cn=DirManager,dc=kijanowski,dc=eu"
    rootpw secret
    directory /data/openldap-2.3.39/var/openldap-data
    index objectClass eq

    TLSCipherSuite HIGH:MEDIUM:-SSLv2
    TLSCACertificateFile /data/openldap-2.3.39/ssl/server.pem
    TLSCertificateFile /data/openldap-2.3.39/ssl/server.pem
    TLSCertificateKeyFile /data/openldap-2.3.39/ssl/server.pem
    TLSVerifyClient never
    The rootpw attribute should be changed from 'secret' to:
    $ /data/openldap-2.3.39/sbin/slappasswd -s admin123
    where 'admin123' is the new directory manager's password. For better performance, you can create a config file for the backend database or copy the sample configuration file like so:
    $ cp /data/openldap-2.3.39/var/openldap-data/DB_CONFIG.example /data/openldap-2.3.39/var/openldap-data/DB_CONFIG
    To start the server with a customized listener, run:
    $ /data/openldap-2.3.39/libexec/slapd -h ldaps://localhost:16636
    You can make sure your LDAP server is up and running (listening) by running:
    $ netstat -an|grep 16636
    tcp 0 0 127.0.0.1:16636 0.0.0.0:* LISTEN
    To create tree like the one shown above, we need to add the following myorg.ldif file:
    dn: dc=kijanowski,dc=eu
    objectclass: top
    objectclass: dcObject
    objectclass: organization
    dc: kijanowski
    o: kijanowski

    dn: o=guvnor,dc=kijanowski,dc=eu
    objectclass: top
    objectclass: organization
    o: guvnor

    dn: ou=People,o=guvnor,dc=kijanowski,dc=eu
    objectclass: top
    objectclass: organizationalUnit
    ou: People

    dn: uid=admin,ou=People,o=guvnor,dc=kijanowski,dc=eu
    objectclass: top
    objectclass: uidObject
    objectclass: person
    objectClass: inetOrgPerson
    uid: admin
    cn: Guvnor Admin
    sn: Administrator
    userPassword: {SSHA}ZGUjbzh0wN0JoWxIAcZfFXpV5MIu/gZw

    dn: uid=user1,ou=People,o=guvnor,dc=kijanowski,dc=eu
    objectclass: top
    objectclass: uidObject
    objectclass: person
    objectClass: inetOrgPerson
    uid: user1
    cn: Regular User
    sn: Regular
    userPassword: {SSHA}Gcif1SlGPu2vHrtoLGYlKXbKBytJiVVF

    dn: ou=Roles,o=guvnor,dc=kijanowski,dc=eu
    objectClass: top
    objectClass: organizationalUnit
    ou: Roles

    dn: cn=admin,ou=Roles,o=guvnor,dc=kijanowski,dc=eu
    objectClass: top
    objectClass: groupOfNames
    cn: admin
    description: the GuvnorAdmin group
    member: uid=admin,ou=People,o=guvnor,dc=kijanowski,dc=eu

    dn: cn=regular,ou=Roles,o=guvnor,dc=kijanowski,dc=eu
    objectClass: top
    objectClass: groupOfNames
    cn: regular
    description: the Guvnor Regular group
    member: uid=user1,ou=People,o=guvnor,dc=kijanowski,dc=eu
    The passwords for admin and user1 are '9uvn04' and 'user1' (respectively) and were generated with slappasswd. To add this ldif to our directory, we will use an ldap client application called ldapadd. First we need to update its configuration to be able to talk over SSL. Edit the file /data/openldap-2.3.39/etc/openldap/ldap.conf and add following line:
    TLS_REQCERT allow
    This will prevent us from getting errors like these:
    client side:
    ldap_initialize( ldaps://localhost:16636 )
    ldap_bind: Can't contact LDAP server (-1)
    additional info: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

    server side:
    TLS trace: SSL3 alert read:fatal:unknown CA
    TLS trace: SSL_accept:failed in SSLv3 read client certificate A
    TLS: can't accept.
    TLS: error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca s3_pkt.c:1057
    connection_read(11): TLS accept failure error=-1 id=4, closing
    connection_closing: readying conn=4 sd=11 for close
    connection_close: conn=4 sd=11
    Now we can add the ldif file to our directory:
    $ /data/openldap-2.3.39/bin/ldapadd -x -D "cn=DirManager,dc=kijanowski,dc=eu" -H ldaps://localhost:16636 -w admin123 -f myorg.ldif

    adding new entry "dc=kijanowski,dc=eu"
    adding new entry "o=guvnor,dc=kijanowski,dc=eu"
    adding new entry "ou=People,o=guvnor,dc=kijanowski,dc=eu"
    adding new entry "uid=admin,ou=People,o=guvnor,dc=kijanowski,dc=eu"
    adding new entry "uid=user1,ou=People,o=guvnor,dc=kijanowski,dc=eu"
    adding new entry "ou=Roles,o=guvnor,dc=kijanowski,dc=eu"
    adding new entry "cn=admin,ou=Roles,o=guvnor,dc=kijanowski,dc=eu"
    adding new entry "cn=regular,ou=Roles,o=guvnor,dc=kijanowski,dc=eu"
    The last step is to configure JAAS in $JBOSS_SERVER/conf/login-config.xml. Replace the previous file based login module with this one:
    <application-policy name="guvnor">
    <authentication>
    <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required" >
    <module-option name="java.naming.provider.url">ldaps://localhost:16636</module-option>
    <module-option name="java.naming.security.protocol">ssl</module-option>
    <module-option name="bindDN">cn=DirManager,dc=kijanowski,dc=eu</module-option>
    <module-option name="bindCredential">admin123</module-option>

    <module-option name="baseCtxDN">ou=People,o=guvnor,dc=kijanowski,dc=eu</module-option>
    <module-option name="baseFilter">(uid={0})</module-option>

    <module-option name="rolesCtxDN">ou=Roles,o=guvnor,dc=kijanowski,dc=eu</module-option>
    <module-option name="roleFilter">(member={1})</module-option>
    <module-option name="roleAttributeID">cn</module-option>

    <module-option name="roleRecursion">-1</module-option>
    <module-option name="searchScope">ONELEVEL_SCOPE</module-option>
    </login-module>
    </authentication>
    </application-policy>
    Now restart JBoss AS and try to login as admin with password 9uvn04. The application server will talk with the OpenLDAP server over SSL. If you want to shutdown the OpenLDAP server you need to determine its PID and interrupt it by sending the process a SIGINT signal:
    $ kill -INT `cat /data/openldap-2.3.39/var/run/slapd.pid`
  9. Use MySQL as a data repository

  10. Jackrabbit has been chosen as a JCR [ Java Content Repository ] implementation. By default, it uses the Derby database as a backend. You may want to switch to a database you are more familiar with, and can regularly back up and properly tune. If you have already used the file-based repository and don't want to loose all your assets, export them. After MySQL is up and running, import them back. To export your current repository, go to the 'Administration' menu on the left side, expand 'Admin,' choose 'Import/Export,' and click on 'Export':


    Shut down the server and set up MySQL as your new repository. First, download MySQL and extract it. I will use the community server 5.0.51a standard extracted to /data/mysql-5.0.51. As root, perform the following steps. (For more details, have a look at the INSTALL-BINARY file):
    $ /usr/sbin/groupadd mysql5
    $ /usr/sbin/useradd -g mysql5 mysql5
    $ cd /data/mysql-5.0.51
    $ chown -R mysql5 .
    $ chgrp -R mysql5 .
    $ /data/mysql-5.0.51/scripts/mysql_install_db --user=mysql5

    $ chown -R root .
    $ chown -R mysql5 data

    # now start MySQL
    $ /data/mysql-5.0.51/bin/mysqld_safe --user=mysql5 &

    # and create a password for root
    $ /data/mysql-5.0.51/bin/mysqladmin -u root password mysqladminpwd
    To shutdown the MySQL server run:
    $ /data/mysql-5.0.51/bin/mysqladmin -u root shutdown -p
    Logout as root and log in to MySQL to create a user and database for Guvnor:
    $ /data/mysql-5.0.51/bin/mysql -u root -p
    mysql> create database guvnor;
    Query OK, 1 row affected (0.00 sec)

    mysql> grant all privileges on guvnor.* to 'guvnor-user'@'localhost' identified by 'guvnor-pwd';
    Query OK, 0 rows affected (0.00 sec)

    mysql> flush privileges;
    Query OK, 0 rows affected (0.00 sec)
    Now the DB side is complete.

    Edit the $GUVNOR/WEB-INF/components.xml file and provide a path to where you would like to keep the repository configuration files. You can leave the default value - which is the JBoss Application Server's bin directory - however it is recommended to provide a location that is regularly backed up. Under the 'repositoryConfiguration' component add:
    <property name="homeDirectory">/data/GuvnorRepo/</property>
    This last step creates a repository.xml file. It is created by default when running Guvnor the first time and is placed into the AS bin directory. This file configures the data repository. We would like to use MySQL, so we will create /data/GuvnorRepo/repository.xml:
    <?xml version="1.0"?>
    <!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.4//EN"
    "http://jackrabbit.apache.org/dtd/repository-1.4.dtd">
    <Repository>

    <!-- Define where to store global data -->
    <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
    <param name="driver" value="com.mysql.jdbc.Driver"/>
    <param name="url" value="jdbc:mysql://localhost:3306/guvnor" />
    <param name="user" value="guvnor-user" />
    <param name="password" value="guvnor-pwd" />
    <param name="schema" value="mysql"/>
    <param name="schemaObjectPrefix" value="Repository_FS_"/>
    </FileSystem>

    <Security appName="Jackrabbit">
    <AccessManager class="org.apache.jackrabbit.core.security.SimpleAccessManager">
    </AccessManager>

    <LoginModule class="org.apache.jackrabbit.core.security.SimpleLoginModule">
    </LoginModule>
    </Security>

    <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>

    <Workspace name="${wsp.name}">

    <PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager">
    <param name="driver" value="com.mysql.jdbc.Driver"/>
    <param name="url" value="jdbc:mysql://localhost:3306/guvnor" />
    <param name="user" value="guvnor-user" />
    <param name="password" value="guvnor-pwd" />
    <param name="schema" value="mysql"/>
    <param name="schemaObjectPrefix" value="WS_PM_${wsp.name}_" />
    <!-- param name="externalBLOBs" value="false" /-->
    </PersistenceManager>

    <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
    <param name="driver" value="com.mysql.jdbc.Driver"/>
    <param name="url" value="jdbc:mysql://localhost:3306/guvnor" />
    <param name="user" value="guvnor-user" />
    <param name="password" value="guvnor-pwd" />
    <param name="schema" value="mysql"/>
    <param name="schemaObjectPrefix" value="WS_FS_${wsp.name}_"/>
    </FileSystem>

    <!--
    Search index and the file system it uses.
    class: FQN of class implementing the QueryHandler interface
    -->
    <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
    <param name="path" value="${wsp.home}/index"/>
    <param name="textFilterClasses" value="org.apache.jackrabbit.extractor.MsWordTextExtractor,org.apache.jackrabbit.extractor.MsExcelTextExtractor,org.apache.jackrabbit.extractor.MsPowerPointTextExtractor,org.apache.jackrabbit.extractor.PdfTextExtractor,org.apache.jackrabbit.extractor.OpenOfficeTextExtractor,org.apache.jackrabbit.extractor.RTFTextExtractor,org.apache.jackrabbit.extractor.HTMLTextExtractor,org.apache.jackrabbit.extractor.XMLTextExtractor"/>
    <param name="extractorPoolSize" value="2"/>
    <param name="supportHighlighting" value="true"/>
    </SearchIndex>
    </Workspace>

    <Versioning rootPath="${rep.home}/version">

    <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
    <param name="driver" value="com.mysql.jdbc.Driver"/>
    <param name="url" value="jdbc:mysql://localhost:3306/guvnor" />
    <param name="user" value="guvnor-user" />
    <param name="password" value="guvnor-pwd" />
    <param name="schema" value="mysql"/>
    <param name="schemaObjectPrefix" value="Versoning_FS_"/>
    </FileSystem>

    <PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager">
    <param name="driver" value="com.mysql.jdbc.Driver"/>
    <param name="url" value="jdbc:mysql://localhost:3306/guvnor" />
    <param name="user" value="guvnor-user" />
    <param name="password" value="guvnor-pwd" />
    <param name="schema" value="mysql"/>
    <param name="schemaObjectPrefix" value="Versioning_PM_" />
    <param name="externalBLOBs" value="false" />
    </PersistenceManager>
    </Versioning>

    <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
    <param name="path" value="${rep.home}/repository/index"/>
    <param name="textFilterClasses" value="org.apache.jackrabbit.extractor.MsWordTextExtractor,org.apache.jackrabbit.extractor.MsExcelTextExtractor,org.apache.jackrabbit.extractor.MsPowerPointTextExtractor,org.apache.jackrabbit.extractor.PdfTextExtractor,org.apache.jackrabbit.extractor.OpenOfficeTextExtractor,org.apache.jackrabbit.extractor.RTFTextExtractor,org.apache.jackrabbit.extractor.HTMLTextExtractor,org.apache.jackrabbit.extractor.XMLTextExtractor"/>
    <param name="extractorPoolSize" value="2"/>
    <param name="supportHighlighting" value="true"/>
    </SearchIndex>

    </Repository>
    As you see we're using the com.mysql.jdbc.Driver, so we need to provide it. Download the mysql java connector from MySQL, unzip it, and copy the JAR file to $JBOSS_SERVER/lib. (I'm using mysql-connector-java-5.1.6).

    Now you can start the app server. If you have exported your assets, just go to the 'Administration' menu, expand 'Admin,' choose 'Import/Export,' and import your xml file. Please note that you have to unzip your exported xml file before you can upload it.

  11. Enable SSL

  12. The last tweak is enabling SSL. It not only provides security, but also ensures the transmitted data hasn't been modified. This is strictly a server-side task.
    First, we need a certificate. Please note that as the "first and last name" you have to provide the full qualified domain name of the host. For testing purposes you can use localhost like so:
    $ keytool -genkey -alias guvnor -keyalg RSA -keystore $JBOSS_SERVER/conf/guvnor.keystore -validity 365

    Enter keystore password: guvnorkspwd
    What is your first and last name?
    [Unknown]: localhost
    What is the name of your organizational unit?
    [Unknown]: My Department
    What is the name of your organization?
    [Unknown]: My Company
    What is the name of your City or Locality?
    [Unknown]: My City
    What is the name of your State or Province?
    [Unknown]: My State
    What is the two-letter country code for this unit?
    [Unknown]: US
    Is CN=My Name, OU=My Department, O=My Company, L=My City, ST=My State, C=US correct?
    [no]: yes

    Enter key password for <guvnor>
    (RETURN if same as keystore password):
    Now we can enable an SSL connector. Edit the file $JBOSS_SERVER/deploy/jboss-web.deployer/server.xml:
        <Connector port="8443" SSLEnabled="true"
    maxThreads="150" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS"
    keystoreFile="${jboss.server.home.dir}/conf/guvnor.keystore"
    keystorePass="guvnorkspwd"/>
    Restart your JBoss Application Server. Guvnor should be
    available at https://localhost:8443/drools-guvnor

  13. How to use a secured Guvnor package

  14. The last part of this article shows how you can access a drools package from Guvnor in a secure way. This is very straight forward if you use certificates signed by trusted authorities. In our test environment, it's a little bit more complicated since we use self-signed certificates.

    First create a package and deploy it. I'll use the package we made during the quick introduction. This package is available under https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST. Replace the url attribute with the new value in Guvnor.properties:
    url=https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST
    One would expect that running the drools application should end successfully, however this is not the case:
    RuleAgent(default) INFO (Wed Jul 23 20:38:03 CEST 2008): Configuring with newInstance=false, secondsToRefresh=-1
    RuleAgent(default) INFO (Wed Jul 23 20:38:03 CEST 2008): Configuring package provider : URLScanner monitoring URLs: https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST
    RuleAgent(default) WARNING (Wed Jul 23 20:38:04 CEST 2008): Was an error contacting https://localhost:8443/drools-guvnor/org.drools.guvnor.Guvnor/package/myNewPackage/LATEST. Reponse header: {}
    RuleAgent(default) EXCEPTION (Wed Jul 23 20:38:04 CEST 2008): Was unable to reach server.. Stack trace should follow.
    java.io.IOException: Was unable to reach server.
    at org.drools.agent.URLScanner.hasChanged(URLScanner.java:149)
    at org.drools.agent.URLScanner.getChangeSet(URLScanner.java:113)
    at org.drools.agent.URLScanner.loadPackageChanges(URLScanner.java:90)
    at org.drools.agent.RuleAgent.checkForChanges(RuleAgent.java:341)
    at org.drools.agent.RuleAgent.refreshRuleBase(RuleAgent.java:300)
    at org.drools.agent.RuleAgent.configure(RuleAgent.java:285)
    at org.drools.agent.RuleAgent.init(RuleAgent.java:209)
    at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:177)
    at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:149)
    at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:217)
    at kijanowski.eu.GuvnorTest.main(GuvnorTest.java:12)
    Exception in thread "main" java.lang.NullPointerException
    at org.drools.agent.RuleAgent.refreshRuleBase(RuleAgent.java:301)
    at org.drools.agent.RuleAgent.configure(RuleAgent.java:285)
    at org.drools.agent.RuleAgent.init(RuleAgent.java:209)
    at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:177)
    at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:149)
    at org.drools.agent.RuleAgent.newRuleAgent(RuleAgent.java:217)
    at kijanowski.eu.GuvnorTest.main(GuvnorTest.java:12)
    If you navigate with your browser to this URL, you will be asked to accept the non-signed certificate. In case of our test application, we need to trust the server by importing its public key to our (temporary) local keystore:
    $ mkdir /data/ssl

    # export the public key
    $ keytool -export -alias guvnor -keystore $JBOSS_SERVER/conf/guvnor.keystore -file /data/ssl/out.cert

    # you don't have to provide a password
    Enter keystore password:

    ***************** WARNING WARNING WARNING *****************
    * The integrity of the information stored in your keystore *
    * has NOT been verified! In order to verify its integrity, *
    * you must provide your keystore password. *
    ***************** WARNING WARNING WARNING *****************

    Certificate stored in file </data/ssl/out.cert>

    # import this key to a local truststore
    $ keytool -import -alias guvnor -file /data/ssl/out.cert -keystore /data/ssl/myKS

    Enter keystore password: qwerty
    Owner: CN=localhost, OU=My Department, O=My Company, L=My City, ST=My State, C=US
    Issuer: CN=localhost, OU=My Department, O=My Company, L=My City, ST=My State, C=US
    Serial number: 48862cb6
    Valid from: Tue Jul 22 20:53:42 CEST 2008 until: Wed Jul 22 20:53:42 CEST 2009
    Certificate fingerprints:
    MD5: 81:FD:97:97:12:E7:2B:94:DA:62:35:11:2C:2B:4E:2B
    SHA1: 7E:B1:36:F4:C9:F9:45:5A:98:F2:F1:46:F6:58:E6:0D:81:46:EC:B5
    Trust this certificate? [no]: yes
    Certificate was added to keystore
    Now we have a keystore with a server's key that we trust. To use this keystore, just start the drools application with the following property:
    -Djavax.net.ssl.trustStore=/data/ssl/myKS
    In Eclipse, click on GuvnorTest.java. From the menu Run -> Open Run Dialog and then add this property to the VM arguments:


    Now your Drools application runs in a secure environment.

  15. Summary

  16. This article has shown how you can upgrade your BRMS, which should only be deployed out-of-the-box for testing purposes. For a multiuser environment with mission-critical applications, Guvnor should be tuned. Have a look at this blog post, the JSSE Reference Guide or the key tool docs page for more details about the tools we used. For LDAP browsing I recommend this very user-friendly and light-weight tool.

Friday, August 15, 2008

Drools logo update

Cheyenne has been doing some more work on the logos for us, this time working on a combined set for the main manual front cover and t-shirts. What do you think?



See previous posting for the standalone images.

Wednesday, August 13, 2008

Screenshot of some new Drools Flow features

Drools Flow has evolved into a powerful workflow language for describing executable business processes. It is different from other existing workflow languages in that it allows advanced integration between processes and rules and offers unified interfaces and tooling to end users. The complexity of your business logic can be reduced and the agility of your processes can be improved by using rules inside processes in a smart way for managing a part of your logic.

The expressiveness of the Drools Flow language has been extended significantly in our latest version to support advanced business process requirements. This includes support for long-running processes (e.g. using states), human task management (and assignment), integration with external services, timers, exception handling, persistence, etc. The following screenshot shows for example two of our newest features: intermediate events and composite nodes.



Intermediate events (using a star icon) allow you to respond to events that are external to the process itself but may influence its flow. Events have a type (e.g. orderAborted) and can carry additional data that can be accessed inside the process once the event has occured. If a large number of events can occur during the execution of your process, using rules inside your process to handle these events and select the appropriate action can greatly simplify your process definition as well.

A ForEach node is an example of a composite node that allows you to execute a part of your process for each element in a collection (e.g. process each of the order items in an order separately). The process can wait for the completion of that sub-flow for each of the elements in the collection before continuing.

Another key feature is the integration with external services using (declarative, domain-specific) work items, but I'll cover that topic in another blog.

You can expect to see the addition of more of these 'patterns' in the future, as we intend to improve the expressiveness of our Drools Flow languages continuously!

Properties Widget In Guvnor

To continue the last post on this topic, the properties editor widget is complete and is in Guvnor code base from now on.


With this widget one can upload a classic properties file, edit it, and also download it back.

Tuesday, August 12, 2008

Machine learning - and Apache Mahout

Isabel Drost recently contributed some enhancements to the Guided Editor (to allow nested facts, very handy) - quite a clever patch.



As if that isn't enough, she is also a contributor to the Apache Mahout project:
Mahout is: (in the projects own words): "Mahout's goal is to build scalable, Apache licensed machine learning libraries." The project site is here.

Interestingly one of my #1 books to read on the toilet at the moment is:


This book talks about (amongst many things) using machine learning to "learn" rules - the benefit of learning rules as opposed to some opaque representation is that a human has a fighting chance of understanding the rules, and improving the learning process. It would be interesting to one day see this stuff applied with projects like Mahout.

Anyway, here is Isabel's writeup on the subject:

The amount of digital data easily available for analysis both in research and
in business has increased tremendously during the last decade. One example of
such data are event logs generated in health care about the patient handling
process. Another example are event logs generated by standard workflow tools.
It is natural to ask whether it is possible to draw conclusions from these
logs, to generalize from what was observed, to learn common process rules
from this data [http://wwwkramer.in.tum.de/ipm08/].

In recent years a rather large community of researchers has treated the
problem of learning from example data. The goal of the new Apache project
Mahout [http://lucene.apache.org/mahout] is to create a commercial friendly,
stable, scalable suite of machine learning tools. The framework is designed
for high throughput and will be capable of handling massive datasets both
during training and application - in case this distinction exists. Our focus
is on scalability and we intend to provide parallelized machine learning
algorithm implementations based on the Hadoop framework.

To date several basic algorithms and frameworks have been implemented and
integrated into Mahout: There are implementations for grouping data points
that are similar to each other (clustering). Based on a set of labeled
examples it is possible to learn a classifier that is able to assign new data
points to existing categories(classification). Mahout also integrated Taste,
a framework for learning which items to recommend to users given a log of
user interactions.

Currently the focus is mainly on recommendation mining and learning from
textual data, yet the community is open for new ideas and happily welcomes
contributions from people involved in other topics. So in case you are
interested in machine learning or want to know what one could do with your
data, just drop by on the dev or users mailing list and post your questions
and comments.

Sunday, August 10, 2008

More Widgets for Guvnor (Anton Arhipov)

For properties and XML editors in Guvnor I'm trying to make use of GWT-Ext components. The widgets from this library look really nice. Also the API looks OK. For the properties widget, theres a PropertiesGridPanel available, which provides some nice features for editing the properties (see the screenshot below).



.. and a piece of code that assembles the panel:
    PropertyGridPanel grid = new PropertyGridPanel();
GridView view = new GridView();
grid.setView(view);
Map map = new HashMap();
PropertyHolder[] props = getProps(); //RPC call here
for (PropertyHolder holder : props.list) {
map.put(holder.name,holder.value);
}
grid.setSource(map);
How does it know which editor should be attached to the value column? Simple the type is known at the compile time! One of the benefits of GWT is claimed to be its performance. It is fast, but this power comes at a price - you cannot leave the type undefined, i.e. you cannot use java.lang.Object for the type of your class members.

Consider the following class, which is intended for exchanging the information via RPC calls:
    public class PropertyHolder implements IsSerializable {
public String name;
public Object value; // coudn't use Object type here
}

While trying to compile this code down to javaScript, we'll get the following messages from the GWT's compiler:
    org.drools.guvnor.client.ruleeditor.PropertyHolder
[java] Analyzing the fields of type 'org.drools.guvnor.client.ruleeditor.PropertyHolder' that qualify for serialization
[java] public java.lang.Object value
[java] java.lang.Object
[java] [ERROR] In order to produce smaller client-side code, 'Object' is not allowed; consider using a more specific type
[java] [ERROR] Type 'org.drools.guvnor.client.ruleeditor.PropertyHolder' was not serializable and has no concrete serializable subtypes

So that's it! I couldn't think of any solution how to cheat the compiler and leave the value's type undefined, so I have to use strings for now.

Thursday, August 07, 2008

Knowledge-based Locomotive Planning for the Swedish Railway

Knowledge-based Locomotive Planning for the Swedish Railway

The link above is the master thesis of Dr. Volker Scholz written in 1998. It discusses the knowledge-based approach for locomotive planning. The algorithms could be adopted and applied to logistic planning in general.

Drools Solver and the upcoming Drools Fusion are well suited for this problem domain.

Tuesday, August 05, 2008

Grails add Drools Plugin

A Drools plug-in for Grails has just been announced the project page for more details on the plug-in can be found here.

Monday, August 04, 2008

Drools wins Bossie award

Drools has won an Infoworld Bossie award for best open source BRMS:
http://www.infoworld.com/slideshow/2008/08/166-best_of_open_so-6.html