All posts by mrules-admin

Version 2.1.0 of MRules is out

We are proud to announce that the version 2.1.0 of MRules is released.

The changes related to this release focus mainly on our functional grammar engine. Many optimizations and new possibilities have been brought. Also, the autocompletion feature has been improved to be more accurate and intuitive.

Some optimizations and new features have also been added to the rules engine.

The release notes provides details on all modifications.

Major Version 2.0.0 of MRules is out

We are proud to announce that the version 2.0.0 of MRules has been released!

Major changes arrive with this new version, among which:

  • New products and extensions, transforming the MRules offer into a coherent and integrated set of products adapted to the needs of our customers.
  • A reorganization of the site to adapt to this new offer.
  • Technical changes on the packaging to facilitate dependency management and integration of our products.
  • And of course, evolutions and fixes coming along with these changes.

The release notes provides details on all modifications.

Please note that the fixes will be back-ported to the LTS version shortly.

Version 1.10.1 of MRules is out

MRules version 1.10.1 has been released and corrects two customer tickets:

  • Performances issues during compilation process, occuring only with specific environments.
  • Regression introduced in version 1.10.0, leading sometimes to a NullPointerException while performing operations on dates, depending on the configuration of the operation.

Please update your dependencies to stay up-to-date.

 

Migrating MRules 1.9.x to 1.10.0

Globally, MRules upgrade will have no impact for existing rules. It will allow to have more features available.

However, if some of you rules perform average computations, minor adaptations might be necessary. Only calculations involving coefficients are impacted. These are minor adjustments which consist in:

  • Rename the “coefficients” property into “coefficientsSource”.
  • Rename the “coefficient” property into “coefficientsProperty”.

Changing these names allowed us to make them consistent with those of the properties of other mathematical operations, as well as to add new calculation possibilities.

Version 1.10.0 of MRules is released

We are proud to announce that the version 1.10.0 of MRules is released.

This version brings lots of new features and improvements. Amongst these, a breaking change on average computations should be noted and taken into account if you are using this functionality. A dedicated post has been written to describe changes.

If you want to know what’s included in version 1.10.0, the release notes provide details on all modifications.

Also, version 1.10.0 will be the last major 1.x release. It paves the way for the version 2.0.0, which is planned to be released at the beginning of the year 2018.

This does not mean that versions 1.x will not be supported. Minor fix versions will be built if necessary. But our efforts are now concentrated on the development of the version 2.0.0, which will include lots of very exciting changes, one of which being a new way of configuring rulesets, by writing rules “as you talk”, with a fully functional language.

We’ll tell you more in few time. Stay tuned.

How-To #4: Data access framework usage

Introduction

This article will provide you with details on how to use MRules built-in layer allowing reading and writing data from or to Java beans.

This layer can be used in a standalone way, it’s not coupled to the rule engine itself.

If you are familiar with anyone of the popular Java frameworks using reflection to access data layer, you won’t be surprised by the syntax, even if it’s slightly different sometimes because it offers more possibilities.

How it works

Concept

The property to access is represented by a String. For example:
myProperty.myNestedProperty“.

This String must be processed, meaning parsed and transformed to a specific Object. We call this step “compiling” the property. This is the role of the IPropertyCompiler utility.

When the String representation of the property is compiled, you obtain an instance of a class implementing the interface IProperty. This resulting instance is immutable and might be used several times, in a multi-threaded environment, to get or set the targeted property.

For example:

package com.massa.mrules.demos.site;

import org.junit.Assert;
import org.junit.Test;

import com.massa.util.ConfigDiscovery;
import com.massa.util.property.IProperty;
import com.massa.util.property.PropertySource;

/**
* Shows how to get and set a property from a Java Bean.
*/
public class PropertyExample {
    private String myProperty = "hello";
    @Test
    public void propertyExample() throws Exception {
        final IProperty property = ConfigDiscovery.getPropertyCompiler().compile("myProperty");
        Assert.assertEquals("hello", property.get(new PropertySource(this)));
        property.set(new PropertySource(this), "Hello World !");
        Assert.assertEquals("Hello World !", this.getMyProperty());
    }

    public String getMyProperty() {
        return this.myProperty;
    }
    public void setMyProperty(final String myProperty) {
        this.myProperty = myProperty;
    }
}

What is possible?

The property access framework shipped with MRules allows to perform all of the following operations:

Reading:

  • Read an unlimited number of nested properties in an Objects hierarchy.
  • Read Arrays elements, whatever is the number of dimensions of the array. The index of the element to read might be a constant or a value read from another property.
  • Lists are handled in the same way as Arrays.
  • Read Maps values, given the key. The key of the value to read might be a constant or a value read from another property.
  • Read instance fields without getter.
  • Invoke methods with or without arguments. The arguments might be constants or values read from another properties.
  • Read static fields.
  • Invoke static methods with or without arguments.
  • Combine all the above, whatever is the protection of the element to access (even if private).
  • Extra: Arrays length can be accessed with the “length” keyword used as if it were a direct field access.

Writing:

  • Write any property of a bean in an Objects hierarchy, with no limitation of level.
  • Write an element of an Array. The Array will be automatically adjusted if too small.
  • Lists are handled in the same way as Arrays.
  • Set Maps values, given the key.
  • Write an instance field, even if private and / or final.
  • Write a static field, even if private. Final static fields can not be written.

Indexed and mapped getters / setters:

Reading or Writing Arrays and Lists might be achieved in two ways:

  • A getter / setter which returns / sets directly the Array or List.
  • An indexed getter / setter, with an integer argument, returning or writing the corresponding element.

The same feature is available for Maps access, with an extra argument corresponding to the key.

This feature allows complying with Apache Common Beanutils possibilities, but we advise to limit its usage for when it’s absolutely necessary and to prefer the standard getters / setters.

Performances

Thanks to the two-phase life cycle of the IProperty beans and to a very optimized code, our framework offers impressive performances for data access: up to 40% faster than the Apache common bean utils.

Moreover, an extension allowing generating bytecode at runtime avoid the usage of reflection for public member accesses, and further improves the execution speed.

Limitations

Current
  • Indexed and mapped getters / setters: only one dimension or level.
Solved
  • Static fields could not be directly accessed until version 2.2.0.
  • Static methods could not be directly accessed until version 2.2.0.
  • Methods with more than one argument could not be directly accessed until version 1.10.0.

Usage

Basics

Reading an object hierarchy is as simple as separating all properties names to traverse with a dot.

For example: “myProperty.myNestedProperty.deeperProperty.alwaysDeeper.leafValue

Mapped properties

Accessing Maps values id done by using parenthesis. For example: “myMap('My Key')” or “myMap("My Key")“.

If the key value comes from another sub-property, just write “myMap(myBean.myKey)“.

Another way to access Mapped properties is to use the same syntax as basic access. For example : “myMap.myKey” will work fine. If the “myMap” property value is not an extension of Map with a “getMyKey()” method, then the framework will intent to use the string “myKey” as the key.

This second way has some disadvantages, so we advise to use the parenthesis syntax :

  • It’s clearer
  • Keys are not limited to Strings
  • Key values can be read from other properties.

Indexed properties

For Lists and Arrays, the syntax is the same. It’s also the same if the Java Bean model used indexes getters / setters or not.

Accessing an Array or List element is achieved by : “myArray['2']” or “myArray["2"]“.

Note: The syntax “myArray[2]” is accepted here, as an integer cannot be interpreted as a sub-property.

If the index value comes from another sub-property, just write “myArray[myBean.myIntegerProperty]“.

Direct access

Instance fields and methods

MRules allows directly accessing fields or invoking methods. The syntax to achieve this operation uses the exclamation mark as a delimiter.

The field name or the method invocation instruction (name + parenthesis + argument) start and end with an exclamation mark.

For example:

  • Field: “!myField!“.
  • Method: “!myMethod()!
    or “!myMethod('My Argument')!
    or “!myMethod(myBean.myArgument)!“.

The last exclamation mark might be omitted when it’s obvious (the dot implies a closing exclamation mark), but take care that it does not change the meaning of the property !

For example:

  • Field: “!myField” is OK.
  • Method: “!myMethod()” is OK.
  • This one cannot be omitted : “!myMapField!("My Key")“.

Extra : the length of an array is accessed using the “!length!” field access: “myArray.!length“.

Static fields and methods

The syntax is very similar to the one used to access instance fields or methods. The access is also preceeded by an exclamation mark. But the whole static access is surrounded by parentheses.

For example:

  • Field: “!(MyClassName.myStaticField)“.
  • Method: “!(MyClassName.myStaticMethod())
    or “!(MyClassName.myStaticMethod('My Argument'))
    or “!(MyClassName.myStaticMethod(myBean.myArgument))
    or even “!(MyClassName.myStaticMethod(!(MyClassName.myStaticField)))“.

Mixing everything!

All possibilities above can be mixed, and allow to adapt to most of the Java Beans models.

Some detailed examples:

  • Accessing a bean, then Map, for which the key comes from the second element of an List: “myBean.myMap(myList[2])“.
  • Accessing a multi-dimensionnal array: “myArray[2][myBean.myIntegerProperty][!myMethod()]“.
  • Retrieving the size of a List: “myBean.myList.!size()“.
  • And even using it to access the element of another list: “myBean.myMainList[myBean.myList.!size()].myListElementProperty“.
  • Using a static access as argument: “myBean.myMainList[!(MyClass.MY_STATIC_INT)].myListElementProperty“.

Almost everything is possible, you are only limited by the complexity of your data model!

Apache Commons connector

You might work with an existing application, making intensive use of Java common bean utils. And you might want to improve speed and user experience.

We developed a connector just for you, allowing to replace part of the common bean utils code with ours. This allows making the legacy code based on bean utils using MRules data access framework, without refactoring.

Just add MRules framework in your classpath, and execute on unique instruction at startup:

org.apache.commons.beanutils.BeanUtilsBean.setInstance(new com.massa.util.commons.MBeanUtilsBean(true);

The boolean “true” indicates that the Apache bean utils syntax, which is slightly different of MRules one, should be used, still to avoid refactoring and unexpected results.

We are working on this connector, to replace more features of the legacy library. But we are progressing carefully, in order to not being introducing unexpected behaviors.

MRules specific syntax

MRules intensively uses the data access framework, as all inputs / outputs are based on Java Beans properties.

To handle features specific to the rule engine, an extended property compiler is used internally and enriches the syntax with the following:

Local variables

They are prefixed with the “$” character and can be read and written.

For example:

  • Accessing a variable: “$myVariable“.
  • Accessing a map with the key as a variable: “myMap($myVariable)".
  • Accessing an array with the index as a variable: “myArray[$myVariable]".
  • Accessing nested properties of a variable: “$myVariable.myNestedProperty“.

The “$” character must be the first of the property (or sub-property). The following is invalid: “myBean.$myVariable“.

Global variables

They are prefixed with the “#” character and are read only. By convention, global variables are upper case. The list of predefined global variables can be found here.

For example:

  • Accessing a variable: “#NOW”.
  • Accessing an map with the index as a variable: “myMap(#RULESETNAME)".
  • Accessing properties of a variable: “#RULESETNAME.!length()“.

The “#” character must be the first of the property  (or sub-property). The following is invalid: “myBean.#READBASE“.

Version 1.9.1 of MRules is out

An issue regarding automatic conversion while searching in collections has be filled by a user. This problem only applies on cases when the type of the objects contained in the collection is not directly available via generics.
Version 1.9.1 corrects this issue and goes further by improving type guessing and automatic conversions in these cases.

Please update your dependencies to stay up-to-date.

How-To #3: Spring Integration

Introduction

This article will show you how to integrate MRules in a project based on Spring. More precisely, we will see here how to use dependency injection with MRules and Spring.

Dependency injection

Dependency injection is a mechanism allowing to implement inversion of control. Programs using dependency injection are loosely coupled, as dependencies between software components are resolved dynamically at runtime instead of being hard coded and static.

MRules supports native CDI specified by Java EE 6 since version 1.3.0. But the Spring implementation is more powerful and can be used with JDK 1.5.

So the version 1.9.0 of MRules is shipped with a brand new extension, providing support for Spring dependency injection. See below how to use it.

Add extension to you project

The extension is shipped as a Maven dependency. A transitive dependency allows to retrieve the library core. Only one dependency declaration is necessary.

POM example for Maven :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <dependencies>
        <!-- Adding dependency -->
        <dependency>
            <groupId>com.massa.mrules.extensions</groupId>
            <artifactId>spring</artifactId>
            <version>1.9.0</version>
        </dependency>
    </dependencies>
</project>

Annotation Configuration

If you are using on your project the new annotation-oriented way to configure Spring (which is in my opinion far more elegant than the old fashioned XML files) then you will have two solutions available to integrate MRules :

With the factory

In the Spring configuration class, you will have to declare a Bean returning an instance of the MRules factory. You can extend the factory class in order to read configuration from external configuration file, database, or any other way. Then Spring will automatically use the factory instance to instanciate the rule engine.
Example :

package com.massa.mrules.demos.spring;

public class MRulesSpringConfig {
    /**
    * Creates a rule engine, managed by Spring via the MRules Factory Bean.
    */
    @Bean
    public MRulesFactoryBean getRuleEngine() throws MConfigurationException {
        final MRulesFactoryBean fact = new MRulesFactoryBean();
        fact.setUri("TEST_SPRING");
        fact.setFactoryImpl(RichXMLFactory.class.getName());
        fact.setConfigHolderImpl(XmlRuleEngineConfigHolder.class.getName());
        fact.setXmlFile("rules.xml");
        return fact;
    }
}

Then, just use your rule engine in your module !

package com.massa.mrules.demos.spring;
public class MRulesSpring {
/**
* Spring will inject your rule engine instance in this field at runtime.
*/
@Autowired
protected IMruleExecutionSet ruleEngine;

// Use the rule engine ...
}

Without the factory

It’s almost the same as the factory way, but you instanciate your rule engine directly in the Spring config class instead of delegating this operation to the factory. And you use you rule engine instance the same way.

It’s a little less flexible, but may be enough depending on the context.

package com.massa.mrules.demos.spring;

public class MRulesSpringConfig {
    /**
    * Creates a rule engine, managed by Spring via the MRules Factory Bean.
    */
    @Bean
    public IMruleExecutionSet getRuleEngine() throws MConfigurationException {
        final InputStream xml = ResourceLoader.getResourceAsStream("rules.xml");
        final RichXMLFactory factory = new RichXMLFactory();
        return factory.read(xml);
    }
}

XML Configuration

If your Spring project is configured using XML, you will have no difficulties to integrate MRules. Just declare a bean in your Spring context configuration, as follow :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Configures rule engine, for Spring XML Demo -->
    <bean id="ruleEngine" class="com.massa.mrules.extensions.spring.MRulesFactoryBean">
        <property name="uri" value="spring test" />
        <property name="configHolderImpl" value="com.massa.mrules.builder.XmlRuleEngineConfigHolder" />
           <property name="factoryImpl" value="com.massa.mrules.factory.xml.RichXMLFactory" />
        <property name="xmlFile" value="rules.xml" />
        <property name="compilationLevel" value="LIGHT" />
    </bean>
</beans>

Then, just use your rule engine in your module, with the @Autowired annotation, or with the XML IOC configuration.

Demo

A demo, which can be found in the download page, illustrates all the examples above.