Chapter 15. Additional Declarations

15.1. Declaring Metadata for Existing Types

JBoss Rules allows the declaration of metadata attributes for existing types in the same way as when declaring metadata attributes for new fact types. The only difference is that there are no fields in that declaration.

15.2. Declaring Metadata for Existing Types Example

This example shows how to declare metadata for an existing type:
import org.drools.examples.Person

declare Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )
end

15.3. Declaring Metadata Using a Fully Qualified Class Name Example

This example shows how you can declare metadata using the fully qualified class name instead of using the import annotation:
declare org.drools.examples.Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )
end

15.4. Parametrized Constructors for Declared Types Example

For a declared type like the following:
declare Person
    firstName : String @key
    lastName : String @key
    age : int
end
The compiler will implicitly generate 3 constructors: one without parameters, one with the @key fields and one with all fields.
Person() // parameterless constructor
Person( String firstName, String lastName )
Person( String firstName, String lastName, int age )

15.5. Non-Typesafe Classes

The @typesafe( <boolean>) annotation has been added to type declarations. By default all type declarations are compiled with type safety enabled. @typesafe( false ) provides a means to override this behaviour by permitting a fall-back, to type unsafe evaluation where all constraints are generated as MVEL constraints and executed dynamically. This is useful when dealing with collections that do not have any generics or mixed type collections.

15.6. Accessing Declared Types from the Application Code

Sometimes applications need to access and handle facts from the declared types. In such cases, JBoss Rules provides a simplified API for the most common fact handling the application wishes to do. A declared fact will belong to the package where it was declared.

15.7. Declaring a Type

This illustrates the process of declaring a type:
package org.drools.examples

import java.util.Date

declare Person
    name : String
    dateOfBirth : Date
    address : Address
end

15.8. Handling Declared Fact Types Through the API Example

This example illustrates the handling of declared fact types through the API:
// get a reference to a knowledge base with a declared type:
KnowledgeBase kbase = ...

// get the declared FactType
FactType personType = kbase.getFactType( "org.drools.examples",
                                         "Person" );

// handle the type as necessary:
// create instances:
Object bob = personType.newInstance();

// set attributes values
personType.set( bob,
                "name",
                "Bob" );
personType.set( bob,
                "age",
                42 );

// insert fact into a session
StatefulKnowledgeSession ksession = ...
ksession.insert( bob );
ksession.fireAllRules();

// read attributes
String name = personType.get( bob, "name" );
int age = personType.get( bob, "age" );

15.9. Type Declaration Extends

Type declarations support the 'extends' keyword for inheritance. To extend a type declared in Java by a DRL declared subtype, repeat the supertype in a declare statement without any fields.

15.10. Type Declaration Extends Example

This illustrates the use of the extends annotation:
import org.people.Person

declare Person
end

declare Student extends Person
    school : String
end

declare LongTermStudent extends Student
    years : int
    course : String
end

15.11. Traits

Traits allow you to model multiple dynamic types which do not fit naturally in a class hierarchy. A trait is an interface that can be applied (and eventually removed) to an individual object at runtime. To create a trait out of an interface, a @format(trait) annotation is added to its declaration in DRL.

15.12. Traits Example

declare GoldenCustomer
    @format(trait)
    // fields will map to getters/setters
    code     : String
    balance  : long
    discount : int
    maxExpense : long
end
In order to apply a trait to an object, the new don keyword is added:
when
    $c : Customer()
then
    GoldenCustomer gc = don( $c, Customer.class );
end

15.13. Core Objects and Traits

When a core object dons a trait, a proxy class is created on the fly (one such class will be generated lazily for each core/trait class combination). The proxy instance, which wraps the core object and implements the trait interface, is inserted automatically and will possibly activate other rules. An immediate advantage of declaring and using interfaces, getting the implementation proxy for free from the engine, is that multiple inheritance hierarchies can be exploited when writing rules. The core classes, however, need not implement any of those interfaces statically, also facilitating the use of legacy classes as cores. Any object can don a trait. For efficiency reasons, however, you can add the @Traitable annotation to a declared bean class to reduce the amount of glue code that the compiler will have to generate. This is optional and will not change the behavior of the engine.

15.14. @Traitable Example

This illustrates the use of the @traitable annotation:
declare Customer
    @Traitable
    code    : String
    balance : long
end

15.15. Writing Rules with Traits

The only connection between core classes and trait interfaces is at the proxy level. (That is, a trait is not specifically tied to a core class.) This means that the same trait can be applied to totally different objects. For this reason, the trait does not transparently expose the fields of its core object. When writing a rule using a trait interface, only the fields of the interface will be available, as usual. However, any field in the interface that corresponds to a core object field, will be mapped by the proxy class.

15.16. Rules with Traits Example

This example illustrates the trait interface being mapped to a field:
when
    $o: OrderItem( $p : price, $code : custCode )
    $c: GoldenCustomer( code == $code, $a : balance, $d: discount )
then
    $c.setBalance( $a - $p*$d );
end

15.17. Hidden Fields

Hidden fields are fields in the core class not exposed by the interface.

15.18. The Two-Part Proxy

The two-part proxy has been developed to deal with soft and hidden fields which are not processed intuitively. Internally, proxies are formed by a proper proxy and a wrapper. The former implements the interface, while the latter manages the core object fields, implementing a name/value map to supports soft fields. The proxy uses both the core object and the map wrapper to implement the interface, as needed.

15.19. Wrappers

The wrapper provides a looser form of typing when writing rules. However, it has also other uses. The wrapper is specific to the object it wraps, regardless of how many traits have been attached to an object. All the proxies on the same object will share the same wrapper. Additionally, the wrapper contains a back-reference to all proxies attached to the wrapped object, effectively allowing traits to see each other.

15.20. Wrapper Example

This is an example of using the wrapper:
when
    $sc : GoldenCustomer( $c : code, // hard getter
                          $maxExpense : maxExpense > 1000 // soft getter
    )
then
    $sc.setDiscount( ... ); // soft setter
end

15.21. Wrapper with isA Annotation Example

This illustrates a wrapper in use with the isA annotation:
$sc : GoldenCustomer( $maxExpense : maxExpense > 1000,
                      this isA "SeniorCustomer"
)

15.22. Removing Traits

The business logic may require that a trait is removed from a wrapped object. There are two ways to do so:
Logical don
Results in a logical insertion of the proxy resulting from the traiting operation.
then
    don( $x, // core object
         Customer.class, // trait class
         true // optional flag for logical insertion
    )
The shed keyword
The shed keyword causes the retraction of the proxy corresponding to the given argument type
then
    Thing t = shed( $x, GoldenCustomer.class )
This operation returns another proxy implementing the org.drools.factmodel.traits.Thing interface, where the getFields() and getCore() methods are defined. Internally, all declared traits are generated to extend this interface (in addition to any others specified). This allows to preserve the wrapper with the soft fields which would otherwise be lost.

15.23. Rule Syntax Example

This is an example of the syntax you should use when creating a rule:
rule "<name>"
    <attribute>*
when
    <conditional element>*
then
    <action>*
end

15.24. Timer Attribute Example

This is what the timer attribute looks like:
timer ( int: <initial delay> <repeat interval>? )
timer ( int: 30s )
timer ( int: 30s 5m )

timer ( cron: <cron expression> )
timer ( cron:* 0/15 * * * ? )

15.25. Timers

The following timers are available in JBoss Rules:
Interval
Interval (indicated by "int:") timers follow the semantics of java.util.Timer objects, with an initial delay and an optional repeat interval.
Cron
Cron (indicated by "cron:") timers follow standard Unix cron expressions.

15.26. Cron Timer Example

This is what the Cron timer looks like:
rule "Send SMS every 15 minutes"
    timer (cron:* 0/15 * * * ?)
when
    $a : Alarm( on == true )
then
    channels[ "sms" ].insert( new Sms( $a.mobileNumber, "The alarm is still on" );
end

15.27. Calendars

Calendars are used to control when rules can fire. JBoss Rules uses the Quartz calendar.

15.28. Quartz Calendar Example

This is what the Quartz calendar looks like:
Calendar weekDayCal = QuartzHelper.quartzCalendarAdapter(org.quartz.Calendar quartzCal)

15.29. Registering a Calendar

Procedure 15.1. Task

  1. Start a StatefulKnowledgeSession.
  2. Use the following code to register the calendar:
    ksession.getCalendars().set( "weekday", weekDayCal );
  3. If you wish to utilize the calendar and a timer together, use the following code:
    rule "weekdays are high priority"
       calendars "weekday"
       timer (int:0 1h)
    when 
        Alarm()
    then
        send( "priority high - we have an alarm” );
    end 
    
    rule "weekend are low priority"
       calendars "weekend"
       timer (int:0 4h)
    when 
        Alarm()
    then
        send( "priority low - we have an alarm” );
    end

15.30. Left Hand Side

The Left Hand Side (LHS) is a common name for the conditional part of the rule. It consists of zero or more Conditional Elements. If the LHS is empty, it will be considered as a condition element that is always true and it will be activated once, when a new WorkingMemory session is created.

15.31. Conditional Elements

Conditional elements work on one or more patterns. The most common conditional element is and. It is implicit when you have multiple patterns in the LHS of a rule that is not connected in any way.

15.32. Rule Without a Conditional Element Example

This is what a rule without a conditional element looks like:
rule "no CEs"
when
    // empty
then
    ... // actions (executed once)
end

// The above rule is internally rewritten as:

rule "eval(true)"
when
    eval( true )
then
    ... // actions (executed once)
end
Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.