Skip to content

Instantly share code, notes, and snippets.

@stephen-masters
Created November 16, 2012 16:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stephen-masters/4088798 to your computer and use it in GitHub Desktop.
Save stephen-masters/4088798 to your computer and use it in GitHub Desktop.
Drools BigDecimal accumulator
package uk.co.scattercode.drools.accumulators;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.math.BigDecimal;
import org.drools.runtime.rule.TypedAccumulateFunction;
/**
* This is a 'sum' accumulator for a collection of {@link BigDecimal}.
* Out-of-the-box Drools has sum accumulators which are able to add up
* BigDecimals, but do so by converting them to doubles. So the number you get
* out at the end is not good if your application is doing anything
* financial.
*
* To use this, create a package builder configuration file:
* <pre>
* META-INF/drools.packagebuilder.conf
* </pre>
* Put the following in it:
* <pre>
* drools.accumulate.function.sumbd = uk.co.scattercode.drools.accumulators.BigDecimalAccumulator
* </pre>
* And you can now do things like:
* <pre>
* accumulate(
* CurrencyExposure( currency in ("GBP", "EUR", "NOK"),
* $exp : exposure
* ),
* $sumGbpEurNok : sumbd( $exp )
* )
* </pre>
*
* @author Stephen Masters
*/
public class BigDecimalAccumulator implements TypedAccumulateFunction {
/**
* Session-specific data required by the accumulator is stored in a
* {@link BigDecimalSum} context, which is instantiated by this
* method.
*/
@Override
public Serializable createContext() {
return new BigDecimalSum();
}
/**
* Initializes the accumulator with an empty list of {@link BigDecimal}.
*/
@Override
public void init(Serializable context) throws Exception {
BigDecimalSum accumulator = (BigDecimalSum) context;
accumulator.init();
}
/**
* Adds the value to the accumulator sum.
*/
@Override
public void accumulate(Serializable context, Object value) {
BigDecimalSum accumulator = (BigDecimalSum) context;
accumulator.add((BigDecimal) value);
}
/**
* Subtracts the value from the accumulator sum.
*/
@Override
public void reverse(Serializable context, Object value) throws Exception {
BigDecimalSum accumulator = (BigDecimalSum) context;
accumulator.subtract((BigDecimal) value);
}
/**
* Yes, this accumulator does implement the reverse method..
*/
@Override
public boolean supportsReverse() {
return true;
}
/**
* Returns the current 'sum' held in the accumulator.
*/
@Override
public Object getResult(Serializable context) throws Exception {
BigDecimalSum accumulator = (BigDecimalSum) context;
return accumulator.sum;
}
/**
* Returns the class of the object returned by getResult.
*/
@Override
public Class<BigDecimal> getResultType() {
return BigDecimal.class;
}
/**
* Required to support {@link Externalizable} interface so that data can be
* shared across sessions. However, we don't need to do that, so this method
* is empty.
*/
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
/**
* Required to support {@link Externalizable} interface so that data can be
* shared across sessions. However, we don't need to do that, so this method
* is empty.
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
/**
* Session-specific data required by the accumulator is stored in a
* an instance of this class.
*/
private static class BigDecimalSum implements Serializable {
/** Generated serialVersionUID */
private static final long serialVersionUID = -3852330030144129793L;
BigDecimal sum = BigDecimal.ZERO;
void init() {
this.sum = BigDecimal.ZERO;
}
void add(BigDecimal augend) {
this.sum = sum.add(augend);
}
void subtract(BigDecimal subtrahend) {
this.sum = sum.subtract(subtrahend);
}
}
}
@stephen-masters
Copy link
Author

Kinda puzzled that I'm not finding examples of doing this all over the place. It seems like an obvious thing that anybody using Drools for financial rules and calculations would probably need at some point. Maybe there are loads of private repositories out there, each with their own implementations? :)

Anyway, this seems to work reasonably well so far. Thoughts on improvements welcomed as always!

@ge0ffrey
Copy link

@stephen-masters
Copy link
Author

Hi Geoffrey - It looks like you have been pushing to get that fixed for a long time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment