Passing Class objects to Drools Rules (drt) file - drools

I'm using Drools Template for decision making, I have created instance of class and passed the class objects to Kie session using insert
In the drt file, I tried to compare the Template columns with Class objects and certain parameters have passed threshold , then setting the Priority based on the value in the Template file
Format of the Template file (Excel)
Parameter Mountpoint Threshold Severity
Disk /root 80 MINOR
Disk /tmp 90 MAJOR
My class Capacity {
private String component;
private String subComponent;
privat Integer value;
public String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
public String getSubComponent() {
return subComponent;
}
public void setSubComponent(String subComponent) {
this.subComponent = subComponent;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public Capacity()
{
super();
this.component = component;
this.subComponent = subComponent;
this.value = value;
}
}
In another class, I'm initiating KieSession and injecting the capacity Class objects and loads the Templates file
Drool
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
try {
loadRuleTemplate(DATA_FILE, RULE_TEMPLATE_FILE, "OperationalMeasurement", 2, 1);
} catch (IOException errorMsg) {
log.error(errorMsg.getMessage()); }
InternalKnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addPackages(kbuilder.getKnowledgePackages());
KieSession kieSession = kbase.newKieSession(sessionConfig, null);
sessionClock = ksession.getSessionClock();
ksession.insert(Capacity);
ksession.fireAllRules();
ksession.dispose();
Rules file
template header
Parameter
Mountpoint
Threshold
Severity
package com.test.drools;
dialect "java"
template "OperationalMeasurement"
rule "Check_Threshold_#{row.rowNumber}"
ruleflow-group "capacityManagement"
salience 500
when
$capacity: Capacity(component == "#{Parameter}" && subComponent == "#{Mountpoint}" && value >= "#{Threshold}")
then
System.out.println("Thresold cross for Component and Priority is set " + #{Severity}");
end
end template
When i run this, component is treated as literal string instead of Capacity class variable and though i dont get any error , it simply prints all the contents of the drt file in the console.
And I dont seems to get the Sysout statements

component is treated as literal string instead of Capacity class variable
Not sure what you mean by that. what "component" is being treated as a literal?
According to the generated rule you added as a comment, there are some errors in the rules being generated:
System.out.println("Thresold crossed for Component::" +component + " and Priority is set " + Major");
In the line above, "component" is expected to be a variable, but there is no variable with that name. There is also a missing quote before the "Major" string.
I would suggest you to change your template to something like this:
rule "Check_Threshold_#{row.rowNumber}"
ruleflow-group "capacityManagement"
salience 500
when
$capacity: Capacity(component == "#{Parameter}" && subComponent == "#{Mountpoint}" && value >= "#{Threshold}")
then
System.out.println("Threshold cross for Component:: #{Parameter} and Priority is set to #{Severity}");
end
end template
That should generate valid DRL.
One other thing that worth mentioning is that for some weird reason that I never fully understood, Drools will not complain if there are errors in your Knowledge Bases (i.e. errors in the DRL files). You have to check for errors by yourself before using a Knowledge Base. The KnowledgeBuilder class has a hasErrors() method and a getResults() methods that you can use to check for errors. If you are using a KieContainer, then you have a verify() method you can use. How are you loading your DRL into the KieBase?
Hope it helps,

Related

How to correctly name a class that contains property that returns value selected from sources set in this class and sets source values thes same way?

I've got a class that contains multiple potential sources of value (of same Type ofc) and property that returns value of some source based on provided sources (in order of importance, including internal value). I'd simply call it Provider, but property can be set also (and this actually sets some source value based on the same logic), so it looks incorrect.
I'd rather call it Two-Way Provider or Contractor (which is cumbersome), but it feels like I've got some pattern here, that has a specific or comprehensive name. More of "near-the-truth" variants: Dispenser, Conductor, Supply, Agent, Contributor. Something as simple as Node, but more specific.
tl;dr: Name must reflect all or most of these statements:
class contains multiple external value sources that can be set "outside" (Exposer???)
class contains "default" internal value (that can be "overriden" by source values) (Container?)
class (property) always provide some value (Provider?)
that (class's) property can be set (Receiver?)
that value, which can be get or set, comes either from external value source (if any), either from internal value (Resolver?)
looks logical next to the Type of value that class (property) presents (example: FloatProvider)
public class FloatProvider //Needs proper name
{
public float internalValue = 1.0f; // these may be renamed
public Foo fooSource = null; // to make more sense
public Bar barSource = null; // in terms of the class
public float Value
{
get => GetValue();
set => SetValue(value);
}
public float GetValue()
{
if (fooSource!=null) return fooSource.value;
else if (barSource!=null) return barSource.value;
else return internalValue;
}
public void SetValue(float value)
{
if (fooSource!=null) fooSource.value = value;
else if (barSource!=null) barSource.value = value;
else internalValue = value;
}
public FloatProvider(float internalValue, Foo fooSource, Bar barSource)
{
this.internalValue = internalValue;
this.fooSource = fooSource;
this.barSource = barSource;
}
}
public class Foo
{
public float value = 10.0f;
}
public class Bar
{
public float value = 0.1f;
}
After days of head scratching and eventual epiphanies I think the Hub is the most appropriate term. But in my case I'd rather call the class by its purpose (FloatExt or something).

How to set values in ItemPreparedStatementSetter for one to many mapping

I am trying to use JdbcBatchItemWriter for a domain object RemittanceClaimVO . RemittanceClaimVO has a List of another domain object , ClaimVO .
public class RemittanceClaimVO {
private long remitId;
private List<ClaimVO> claims = new ArrayList<ClaimVO>();
//setter and getters
}
So for each remit id, there would be multiple claims and I wish to use single batch statement to insert all rows.
With plain jdbc, I used to write this object by putting values in batches like below ,
ist<ClaimVO> claims = remittanceClaimVO.getClaims();
if(claims != null && !claims.isEmpty()){
for(ClaimVO claim:claims){
int counter = 1 ;
stmt.setLong(counter++, remittanceClaimVO.getRemitId());
stmt.setLong(counter++, claim.getClaimId());
stmt.addBatch();
}
}
stmt.executeBatch();
I am not sure how to achieve same in Spring Batch by using ItemPreparedStatementSetter.
I have tried similar loop as above in setValues method but values not getting set.
#Override
public void setValues(RemittanceClaimVO remittanceClaimVO, PreparedStatement ps) throws SQLException {
List<ClaimVO> claims = remittanceClaimVO.getClaims();
for(ClaimVO claim:claims){
int counter = 1 ;
ps.setLong(counter++, remittanceClaimVO.getRemitId());
ps.setLong(counter++, claim.getClaimId());
}
}
This seems another related question.
Please suggest.

Why can't nlog read the current date

I'm using Nlog to write some logging to a textfile. Partial nlog.config:
<target name="file" xsi:type="File" fileName="${basedir}/MBWRunner_log.txt"
layout="${date} (${level}): ${message}
Exception: ${exception:format=Method, ToString}"/>
Lines in the logfile look like this:
0001-01-01 00:00:00 (Trace): MBWRunner started
As you can see the date and time are all 0. I have tested {longdate} and {date:format=yyyyMMddHHmmss} with the same result.
The application is a console app, run from an elevated commandline.
Any clues?
[EDIT] I have tested this on 2 machine's within the organisation with the same result. Please help!
Code used:
static Logger _logger = LogManager.GetCurrentClassLogger();
public static void Log(string message, LogLevel priority)
{
LogEventInfo eventinfo = new LogEventInfo(); ;
eventinfo.Message = message;
eventinfo.Level = priority;
Log(eventinfo);
}
static void Log(LogEventInfo logentry)
{
_logger.Log(logentry);
}
UPDATE:
#edosoft I think the problem is your use of the default constructor for LogEventInfo. If you look at the source for LogEventInfo here
https://github.com/NLog/NLog/blob/master/src/NLog/LogEventInfo.cs
You will see that using the default constructor does not populate the .TimeStamp field, so the field will probably just default to the default value for DateTime, which I assume is DateTime.MinValue. You should use one of the other constructors or one of the Create methods. Since you are setting only the Message and Level fields, I would suggest either:
var logEvent = new LogEventInfo(priority, "", message); //Second param is logger name.
Or
var logEvent = LogEventInfo.Create(priority, "", message);
From the NLog source for DateLayoutRenderer (from here) we can see that the date value that gets written as part of the logging stream is calculated like this:
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
var ts = logEvent.TimeStamp;
if (this.UniversalTime)
{
ts = ts.ToUniversalTime();
}
builder.Append(ts.ToString(this.Format, this.Culture));
}
What is happening here is that the DateLayoutRenderer is getting the TimeStamp value from the LogEventInfo object (NLog creates one of these each time you use the Logger.Trace, Logger.Debug, Logger.Info, etc methods. You can also create LogEventInfo objects yourself and log them with the Logger.Log method).
By default, when a LogEventInfo object is created, its TimeStamp field is set like this (from the source for LogEventInfo here) (note the use of CurrentTimeGetter.Now):
public LogEventInfo(LogLevel level, string loggerName, IFormatProvider formatProvider, [Localizable(false)] string message, object[] parameters, Exception exception)
{
this.TimeStamp = CurrentTimeGetter.Now;
this.Level = level;
this.LoggerName = loggerName;
this.Message = message;
this.Parameters = parameters;
this.FormatProvider = formatProvider;
this.Exception = exception;
this.SequenceID = Interlocked.Increment(ref globalSequenceId);
if (NeedToPreformatMessage(parameters))
{
this.CalcFormattedMessage();
}
}
The TimeStamp field is set in the LogEventInfo constructor using the TimeSource.Current.Now property, whose implementation can be seen here.
(UPDATE - At some point NLog changed from using CurrentTimeGetter to a more generic approach of having a TimeSource object that has several flavors (one of which, CachedTimeSource, is essentially the same as CurrentTimeGetter)).
To save the trouble of navigating the link, here is the source for CachedTimeSource:
public abstract class CachedTimeSource : TimeSource
{
private int lastTicks = -1;
private DateTime lastTime = DateTime.MinValue;
/// <summary>
/// Gets raw uncached time from derived time source.
/// </summary>
protected abstract DateTime FreshTime { get; }
/// <summary>
/// Gets current time cached for one system tick (15.6 milliseconds).
/// </summary>
public override DateTime Time
{
get
{
int tickCount = Environment.TickCount;
if (tickCount == lastTicks)
return lastTime;
else
{
DateTime time = FreshTime;
lastTicks = tickCount;
lastTime = time;
return time;
}
}
}
}
The purpose of this class is to use a relatively cheap operation (Environment.Ticks) to limit access to a relatively expensive operation (DateTime.Now). If the value of Ticks does not change from call to call (from one logged message to the next), then the value of DateTime.Now retrieved the this time will be the same as the value of DateTime.Now retrieved this time, so just use the last retrieved value.
With all of this code in play (and with Date/Time logging apparently working for most other people), one possible explanation of your problem is that you are using the Logger.Log method to log your messages and you are building the LogEventInfo objects yourself. By default, if you just new a LogEventInfo object, the automatic setting of the TimeStamp property should work fine. It is only dependent on Environment.Ticks, DateTime.Now, and the logic that reuses the last DateTime.Now value, if appropriate.
Is it possible that you are creating a LogEventInfo object and then setting its TimeStamp property to DateTime.MinValue? I ask because the date that is being logged is DateTime.MinValue.
The only other explanation that I can think of would be if Environment.Ticks returns -1 for some reason. If it did, then CurrentTimeGetter would always return the initial value of the lastDateTime private member variable. I can't imagine a scenario where Environment.Ticks would return -1.

AspectJ - Is it possible to catch execution of an advice?

I have a CachingAspect which performs some simple caching on properly annotated methods using an around advice. Now, what I want to do is to trace the caching and the around advice in particular.
So far I'm able to intercept method calls within the around advice but not the advice itself. Ultimately, I would want to get the signature of the method the around advice is advising. Is it possible?
Thanks in advance!
What do you mean by
[adviceexecution pointcut] is not working for me
For me it works just fine, like so:
public aspect MetaAspect {
before() : within(DummyAspect) && adviceexecution() {
System.out.println("MetaAspect: " + thisJoinPointStaticPart.getSignature());
for (Object arg : thisJoinPoint.getArgs())
System.out.println(" " + arg);
}
}
From that point, looking at the signatures printed, you should be able to further refine which advice to pick from DummyAspect if there is more than one and they have different signatures.
Update:
Okay, you have edited your question and stated that what you need to determine is not just adviceexecution() but also the intercepted method's signature. There is no 100% solution for that, but if you make sure your intercepted advice somehow refers to methods of thisJoinPointStaticPart, an instance of JoinPoint.StaticPart will be added to the advice's own signature and can be accessed from your meta aspect. Here is a complete code sample:
Driver application:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.writeProperty("fullName", "John Doe");
application.readProperty("firstName");
application.doSomething(11);
}
public void writeProperty(String name, String value) {}
public String readProperty(String name) { return "foo"; }
public void doSomething(int number) {}
}
Caching aspect:
package de.scrum_master.aspect;
public aspect CachingAspect {
pointcut readMethods(String propertyName) :
execution(* *.read*(String)) && args(propertyName);
before(String propertyName) : readMethods(propertyName) {
System.out.println(
"[CachingAspect] Read method called for property '" + propertyName + "'"
);
}
Object around(String propertyName) : readMethods(propertyName) {
System.out.println(
"[CachingAspect] Caching property '" + propertyName +
"' in method " + thisJoinPointStaticPart.getSignature()
);
return proceed(propertyName);
}
}
As you can see, there are two advice in this aspect. The first one does not access any join point members, the second one does. I.e. we will be able to find out the second one's target signature only in our meta aspect.
Meta aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint.StaticPart;
public aspect AdviceInterceptor {
before() : within(CachingAspect) && adviceexecution() {
System.out.println("[AdviceInterceptor] Intercepting " + thisJoinPointStaticPart);
boolean foundSignature = false;
for (Object arg : thisJoinPoint.getArgs()) {
if (arg instanceof StaticPart) {
foundSignature = true;
StaticPart jpStaticPart = (StaticPart) arg;
System.out.println("[AdviceInterceptor] Target method = " + jpStaticPart.getSignature());
break;
}
}
if (!foundSignature)
System.out.println("[AdviceInterceptor] Target method cannot be determined from advice signature");
}
}
The meta advice iterates over its parameters in order to find a JoinPoint.StaticPart type parameter. If it finds one, it prints its target signature, otherwise it prints a failure note after the loop.
Sample output:
[AdviceInterceptor] Intercepting adviceexecution(void de.scrum_master.aspect.CachingAspect.before(String))
[AdviceInterceptor] Target method cannot be determined from advice signature
[CachingAspect] Read method called for property 'firstName'
[AdviceInterceptor] Intercepting adviceexecution(Object de.scrum_master.aspect.CachingAspect.around(String, AroundClosure, JoinPoint.StaticPart))
[AdviceInterceptor] Target method = String de.scrum_master.app.Application.readProperty(String)
[CachingAspect] Caching property 'firstName' in method String de.scrum_master.app.Application.readProperty(String)
See adviceexecution() pointcut.
You can use the thisJoinPoint.getSignature() inside the advice to get the method signature like this:
pointcut tolog1() : execution(* Activity+.*(..)) ;
before() : tolog1() {
String method = thisJoinPoint.getSignature().toShortString();
Log.d(ATAG, "=========== entering " + method+", parms="+Arrays.toString(thisJoinPoint.getArgs()));
}

Is there a way to capitalize the first letter of a value of a variable in Eclipse (Helios) code templates

I have a code template with a variable and I would like to capitalize(just the first letter) the value of this variable only in some occurrences. Is there a way to do this?
The template code is as follows - I would like to capitalize Property Name in my function names...
private $$${PropertyName};
${cursor}
public function get${PropertyName}()
{
return $$this->${PropertyName};
}
public function set${PropertyName}($$value)
{
$$this->${PropertyName} = $$value;
}
Please Note: This is a template for use with code templates in the IDE (not in PHP). For details see: http://www.ibm.com/developerworks/opensource/tutorials/os-eclipse-code-templates/index.html
I also want this and tried to build a custom TemplateVariableResolver to do it. (I already have one custom resolver in place that generates new UUIDs a la http://dev.eclipse.org/blogs/jdtui/2007/12/04/text-templates-2/.)
I made a custom resolver bound to capitalize:
public class CapitalizingVariableResolver extends TemplateVariableResolver {
#Override
public void resolve(TemplateVariable variable, TemplateContext context) {
#SuppressWarnings("unchecked")
final List<String> params = variable.getVariableType().getParams();
if (params.isEmpty())
return;
final String currentValue = context.getVariable(params.get(0));
if (currentValue == null || currentValue.length() == 0)
return;
variable.setValue(currentValue.substring(0, 1).toUpperCase() + currentValue.substring(1));
}
}
(plugin.xml:)
<extension point="org.eclipse.ui.editors.templates">
<resolver
class="com.foo.CapitalizingVariableResolver"
contextTypeId="java"
description="Resolves to the value of the variable named by the first argument, but with its first letter capitalized."
name="capitalized"
type="capitalize">
</resolver>
</extension>
that I would use like this: (I am working in Java; I see that you do not appear to be)
public PropertyAccessor<${propertyType}> ${property:field}() {
return ${property};
}
public ${propertyType} get${capitalizedProperty:capitalize(property)}() {
return ${property}.get();
}
public void set${capitalizedProperty}(${propertyType} ${property}) {
this.${property}.set(${property});
}
As of Eclipse 3.5, the problem I am having is that my custom resolver does not get a chance to re-resolve once I've specified a value for the property variable. It appears that the Java Development Tools (Eclipse JDT) do this dependent template variable re-resolution via a mechanism called MultiVariableGuess within the JavaContext (see addDependency()). Unfortunately for us, that mechanism does not seem to be exposed, so I/we can't use it to do the same (without lots of copy-and-paste or other redundant work).
At this point, I am giving up again for a while and will keep typing the leading-lowercase and leading-uppercase names separately into two independent template variables.