Drools-OSGi Integration Print E-mail
Written by Valery Abu-Eid   
Friday, 17 October 2008 00:11

Integration Scenarios

While Drools (aka. JBoss Rules) already offers mechanisms for updating rules at runtime, OSGi can be used as an alternative/complimentary way to achieve dynamicity and to enable class versioning. Below are some variants of OSGi-Drools integration that seemed most relevant to me:

  • Extending Rule Agent to make it OSGi aware: Drools Rule Agent allows deploying rules in an automated way. The default implementation looks for rules from directories and URLs, you can extend the org.drools.agent.RuleAgent class to make it OSGi aware or simply create a new OSGi-aware Rule Agent. The OSGi-aware Rule Agent can look for rules in new bundles and load the ones that match a certain pattern.
  • Providing rules as OSGi Services: You can provide rules inside your OSGi-based application as OSGi Services that implement the org.drools.RuleBase interface and has service properties that indicate their usage, so you can provide newer rules by providing OSGi Services with higher ranks. On one hand you will have to create the Rule Base every time you want to make an update to your rule, on the other, such approach doesn't have any restrictions on the way rules are updated (for instance, updating rules by only updating the rule packages they contain).

Integration Issues

Prior to working on OSGi-Drools integration scenarios you need to address few concerns:

  • Drools is OSGi-incompliant: Drools JAR files don't contain OSGi headers, as such you can't simply install them like you would do with bundles. There are two solutions for this problem: Using an OSGi-compliant version provided by the SpringSource Repository or using a Bundle Generator like the one provided by DA-Launcher to generate bundles from Drools class libraries and their dependencies.
  • OSGi Environment aware Class Loader: While in non-osgi applications you didn't need to address class/resource loading issues since default Class Loader used to work, in OSGi Environments you need to make sure to specify for Drools an appropriate Class Loader that would load classes and resources from the OSGi Environment.

Example Application

The Example Application consists of four application bundles:

  • Model Bundle: A bundle which provides model classes that will be used in rules.
  • Simple Price Rule Bundle: A bundle which provides a simple price validation rule as an OSGi service.
  • Advanced Price Rule Bundle: A bundle which provides an advanced price validation rule as an OSGi service. The rank of the Advanced Rule Service is higher of the Simple Rule Service, as such, when these bundles are installed together, the Rules Consumer will use the Advanced Price Rule service.
  • Price Rule Consumer Bundle: A bundle which consumes the available Price rules service with highest rank.

The consumer bundle invokes the Price Rule Service (if available) every 5 seconds so the change in the behavior could be noted when Rules Provider bundles are installed and uninstalled at runtime. Note worthy about this example is that dynamicity achieved by providing rules as OSGi Services (the second approach) and that an OSGi-aware Class Loader provided by the ClassLoading-Utils project was used. The code below demonstrates how the rule was created in the OSGi Environment and how I used the OSGi-aware Class Loader.


import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Properties;

import org.drools.RuleBase;
import org.drools.RuleBaseConfiguration;
import org.drools.RuleBaseFactory;
import org.drools.compiler.PackageBuilder;
import org.drools.compiler.PackageBuilderConfiguration;
import org.drools.rule.Package;
import org.dynamicjava.osgi.classloading_utils.OsgiEnvironmentClassLoader;
import org.osgi.framework.BundleContext;

public class RuleCreator {
	
	public RuleBase createRule(BundleContext bundleContext) throws Exception {
		OsgiEnvironmentClassLoader classLoader =
				new OsgiEnvironmentClassLoader(bundleContext,
					Thread.currentThread().getContextClassLoader(),
					bundleContext.getBundle());
		
		Reader drlSource = new InputStreamReader(
				bundleContext.getBundle().getResource(
					"advanced_rule.dslr").openStream());
		Reader dslSource = new InputStreamReader(
				bundleContext.getBundle().getResource(
					"advanced_rule.dsl").openStream());
		
		Properties properties = new Properties();
		properties.setProperty("drools.dialect.java.compiler", "JANINO");
		PackageBuilderConfiguration config =
				new PackageBuilderConfiguration(classLoader, properties);
		PackageBuilder builder = new PackageBuilder(config);
		
		builder.addPackageFromDrl(drlSource, dslSource);
		Package pkg = builder.getPackage();
		
		RuleBaseConfiguration ruleBaseConfig = new RuleBaseConfiguration();
		RuleBase ruleBase = RuleBaseFactory.newRuleBase(ruleBaseConfig);
		ruleBase.addPackage(pkg);
		return ruleBase;
	}
	
}
	

I left the OSGi-incompliant Drools libraries and their dependencies for DA-Launcher to generate bundles from them. The Example Application can be downloaded from the link below.

The example application is available from drools-osgi-example.zip. The source code drools-osgi-example-src.zip. Currently, this example is only compatible with Equinox, the work for supporting Felix and Knopflerfish is in-progress and the example application will be updated soon.