Re: [rules-users] modifying the problem facts while running the solver - Drools Planner
by Garf
spinjala wrote
>
> I tried setting the scoreAttained and maximum time allowed to spend in my
> termination configuration, but none seems to stop the solver finding the
> solution that violates ALL constraints and hence throws a negative hard
> score. Is this the only way to do it or any other way is available to
> force the solver not to look for a solution when ALL constraints are
> violated?
>
>From Planner's perspective, there are multiple possible solutions.
Each solution has a score, a combination of the hard and soft scores, each
of which represents the sum of constraint violations.
If the scores are <0, then the solution is nonoptimal; and thus, it will
keep looking.
You might try the termination/maximumUnimprovedStepCount config setting.
(see section 6.6.4 of the doc)
This stops the solving after a number of steps have been tried without any
improvement to the score.
Jon
--
View this message in context: http://drools.46999.n3.nabble.com/modifying-the-problem-facts-while-runni...
Sent from the Drools: User forum mailing list archive at Nabble.com.
12 years, 4 months
Re: [rules-users] modifying the problem facts while running the solver - Drools Planner
by Garf
spinjala wrote
>
> 1) When a solution violates all constraints, does it provide a default
> solution? If so, can it be changed so that it doesn't provide one?
> Otherwise, is there a way to assure that no solution is generated when ALL
> constraints are violated.
>
Let's walk through the code.
Solver.solve() will look for solutions until any of the conditions specified
by the termination configuration are met. These could be based on time, or
score, etc.
In your case, your best solution will have a negative hard score. You would
find this via:
Solution Sol = solver.getBestSolution();
if (Sol.getScore().getHardScore() == 0) {
// no constraints are violated
} else {
// Sol = null
}
spinjala wrote
>
> 2) When a feasible solution is not generated, is it possible to make a
> custom solution as the most relevant solution instead of the generated
> one? For ex. if the solution generated by Drools planner assigned a work
> order WO2 to a resource R1, is it possible to create a custom solution
> that assigns it to, possibly R2?
>
Sharpen your grammar here.
Your first sentence's first clause says that the feasible solution is not
generated, but the last clause references "the generated one."
So who (or what) is making this custom solution?
Remember that the Solution is your class. You are free to write your own
code to fill it on your own via your own code (or user actions).
Jon
--
View this message in context: http://drools.46999.n3.nabble.com/modifying-the-problem-facts-while-runni...
Sent from the Drools: User forum mailing list archive at Nabble.com.
12 years, 4 months
Getting Null pointer exception for applying change set
by Rana
Having trouble getting the URL, getting null when getting the ChangeSet file.
Please let me know what is the problem.
ResourceChangeScannerConfiguration conf =
ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();
conf.setProperty("drools.resource.scanner.interval", "60");
log.info("Drools Resource Scanner is set to 60 seconds");
ResourceFactory.getResourceChangeScannerService().configure(conf);
ResourceFactory.getResourceChangeScannerService().start();
log.info("Drools Resource Change Scanner Service has started");
ResourceFactory.getResourceChangeNotifierService().start();
log.info("Drools Resource Change Notifier Service has started");
URL url =
this.getClass().getResource("/src/main/resources/DroolsChangeSet.xml");
URLClassLoader customURLClassloader = new URLClassLoader(new URL[]
{url }, this.getClass().getClassLoader());
KnowledgeBuilderConfiguration kbuilderConfig =
KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null,
customURLClassloader);
// KnowledgeBuilder kbuilder =
KnowledgeBuilderFactory.newKnowledgeBuilder(kbuilderConfig);
KnowledgeBaseConfiguration kbaseConfig =
KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null,
customURLClassloader);
kbaseConfig.setOption( EventProcessingOption.STREAM );
kbaseConfig.setOption( MultithreadEvaluationOption.YES );
// kbaseConfig.setOption( MaxThreadsOption.get(2) );
kbase = KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig);
// create a knowledge agent
KnowledgeAgentConfiguration aconf =
KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
aconf.setProperty("drools.agent.scanDirectories", "true");
aconf.setProperty("drools.agent.newInstance", "false");
// KnowledgeAgent kAgent =
KnowledgeAgentFactory.newKnowledgeAgent("Drools Knowledge Agent", null,
aconf);
KnowledgeAgent kagent =
KnowledgeAgentFactory.newKnowledgeAgent("Drools Knowledge Agent", kbase,
aconf, kbuilderConfig);
kagent.applyChangeSet(ResourceFactory.newUrlResource(url));
log.info("Knowledge Agent Created for all the Drools Files");
// load up the knowledge base
kbase = kagent.getKnowledgeBase();
Thanks.
--
View this message in context: http://drools.46999.n3.nabble.com/Getting-Null-pointer-exception-for-appl...
Sent from the Drools: User forum mailing list archive at Nabble.com.
12 years, 4 months
Creating template column in guvnor for attribute value?
by dunnlow
I'm using Guvnor 5.4
I find myself sometimes wanting to use templates to map a string to a
metadata value or timer. For example, based upon a name, set a value for
the timeout. As far as I can tell however, the only way to create a
template column in guvnor is to include the column identifier in the LHS.
For example, I have a basic rule template that checks a name (there is only
a "name" column in the template matrix). Then, I click on options, add a
timer and give it a value of "@{timeout}" I get an error - the timeout
column does not exist in the template. However, if I write a dummy
expression on the LHS like "foo" not matches timeout, the timeout column is
created and everything is great once I set the value. If I delete the
predicate that uses timeout, I get an error again for the timer attribute.
This gets really ugly because I end up having (seemingly) useless
predicates. Is there a clean way to support this functionality?
Thanks,
-J
--
View this message in context: http://drools.46999.n3.nabble.com/Creating-template-column-in-guvnor-for-...
Sent from the Drools: User forum mailing list archive at Nabble.com.
12 years, 4 months
Drools 6 - approximate release date?
by Michal Bali
Hi guys,
anybody knows when is Drools version 6 going to be released? In a year?
Year and a half? More?
Thank you in advance.
Best regards,
Michal
12 years, 4 months
Events do not get removed automatically
by Winfried Umbrath
Hi guys,
the drools fusion documentation states that
"One of the benefits of running the engine in STREAM mode is that the
engine can detect when an event can no longer match any rule due to its
temporal constraints. When that happens, the engine can safely retract
the event from the session without side effects and release any
resources used by that event."
I have a hard time understanding why my events do not get removed. In
the following example I have following rules running in stream mode:
----------------------------------------------------------------------------------------------------------------------------
package rules
import java.util.logging.Logger
declare GenericEvent
@role(event)
playerIndex : long
eventName : String
card : String
chosenSuit : String
end
declare Table
currentPlayerIndex : long
topCard : String
end
rule "new table"
when
not Table()
then
insert(new Table(0,"a"));
end
rule "discardChoosing"
when
$event : GenericEvent(eventName=="discardCard", $playerIndex :
playerIndex, card.equals("c")) from entry-point "my stream"
$table : Table(currentPlayerIndex==$playerIndex, !topCard.equals("c"))
then
Logger.getLogger("####").info("discardChoosing FIRED");
modify($table) {setTopCard("c")};
end
rule "choosingSuit"
no-loop
when
$event : GenericEvent(eventName=="choosingSuit", $playerIndex :
playerIndex, $chosenSuit : chosenSuit) from entry-point "my stream"
$table : Table(currentPlayerIndex==$playerIndex)
$discardCardEvent : GenericEvent(eventName=="discardCard",
playerIndex==$playerIndex, card.equals("c")) from entry-point "my stream"
then
Logger.getLogger("####").info("choosingSuit FIRED");
modify($table)
{setCurrentPlayerIndex($table.getCurrentPlayerIndex() + 1)};
end
rule "previousPlayer"
when
$event : GenericEvent(eventName=="previousPlayer") from entry-point
"my stream"
$table : Table()
then
Logger.getLogger("####").info("previousPlayer FIRED");
modify($table)
{setCurrentPlayerIndex($table.getCurrentPlayerIndex() - 1)};
end
----------------------------------------------------------------------------------------------------------------------------
I would expect after inserting "discardCard" followed by "choosingSuit"
event, both should be retracted as their conditions do not evaluate to
true anymore. But even after sleeping a couple of seconds, the rule
"choosingSuit" fires again, which means to me the events are still in
working memory. Here is the log output:
inserting GenericEvent( playerIndex=0, eventName=discardCard, card=c,
chosenSuit=null )
INFO: discardChoosing FIRED
INFO: choosingSuit FIRED
inserting GenericEvent( playerIndex=0, eventName=choosingSuit,
card=null, chosenSuit=HEARTS )
Sleeping 9 seconds...
Sleeping done. Printing facts:
Fact handle: [fact 0:2:1690464956:1690464956:5:DEFAULT:Table(
currentPlayerIndex=1, topCard=c )]
inserting GenericEvent( playerIndex=0, eventName=previousPlayer,
card=null, chosenSuit=null )
INFO: previousPlayer FIRED
INFO: choosingSuit FIRED
INFO: previousPlayer FIRED
INFO: choosingSuit FIRED
INFO: previousPlayer FIRED
INFO: choosingSuit FIRED
INFO: previousPlayer FIRED
INFO: choosingSuit FIRED
INFO: previousPlayer FIRED
...
I'm running Drools 5.4 and inserting/executing in this way:
List cmds = new ArrayList();
cmds.add(CommandFactory.newInsert(discard, "event", false, "my stream"));
cmds.add(CommandFactory.newFireAllRules(20));
System.out.println("inserting " + discard);
ksession.execute(CommandFactory.newBatchExecution(cmds));
Any help is highly appreciated.
12 years, 4 months
MVEL version
by paco
Hello
The rules have been written with guvnor 5.0
I have imported it on guvnor 5.3.0
However I met some syntax errors due to the new version of MVEL.
I can not find new versions of MVEL related to guvnor 5.3.0
Can someone tell me how I can get to know the version of MVEL of guvnor
5.3.0 ?
I’m using guvnor 5.3.0 and java 1.6.0.24
Thank you
--
View this message in context: http://drools.46999.n3.nabble.com/MVEL-version-tp4019113.html
Sent from the Drools: User forum mailing list archive at Nabble.com.
12 years, 4 months
Drools 5.4 - Can't catch Exception
by gboro54
So I keep getting the exceptions below. I have surrounded the fireAllRules
with a try catch throwable block to try and debug this but I never seem to
be able to catch it. This only happens in the first couple 100 invocations
of the fireAllRules then never happens again(sometimes it never happens at
all). Is there a way to handle this exception or prevent it from happening?
I am assuming that this is occurring do to an activation being evaluated
after the OrderSideContext is retracted from working memory. Anyone else run
into a similar issue?
TIA
-Mike
07:48:35,377 ERROR [stderr] (Thread-118) Exception in thread "Thread-118"
java.lang.RuntimeException: unable to invoke method:
com.billing.domain.context.OrderSideContext.getPrimarySide: target of method
is null
07:48:35,377 ERROR [stderr] (Thread-118) at
org.mvel2.optimizers.impl.refl.nodes.GetterAccessor.getValue(GetterAccessor.java:66)
07:48:35,377 ERROR [stderr] (Thread-118) at
org.mvel2.optimizers.impl.refl.nodes.VariableAccessor.getValue(VariableAccessor.java:37)
07:48:35,378 ERROR [stderr] (Thread-118) at
org.mvel2.ast.ASTNode.getReducedValueAccelerated(ASTNode.java:108)
07:48:35,378 ERROR [stderr] (Thread-118) at
org.mvel2.ast.BinaryOperation.getReducedValueAccelerated(BinaryOperation.java:107)
07:48:35,378 ERROR [stderr] (Thread-118) at
org.mvel2.ast.Or.getReducedValueAccelerated(Or.java:34)
07:48:35,378 ERROR [stderr] (Thread-118) at
org.mvel2.compiler.ExecutableAccessor.getValue(ExecutableAccessor.java:38)
07:48:35,378 ERROR [stderr] (Thread-118) at
org.mvel2.ast.Substatement.getReducedValueAccelerated(Substatement.java:44)
07:48:35,378 ERROR [stderr] (Thread-118) at
org.mvel2.ast.And.getReducedValueAccelerated(And.java:34)
07:48:35,378 ERROR [stderr] (Thread-118) at
org.mvel2.MVELRuntime.execute(MVELRuntime.java:85)
07:48:35,379 ERROR [stderr] (Thread-118) at
org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:123)
07:48:35,379 ERROR [stderr] (Thread-118) at
org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:116)
07:48:35,379 ERROR [stderr] (Thread-118) at
org.mvel2.MVEL.executeExpression(MVEL.java:930)
07:48:35,379 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConditionEvaluator.evaluate(MvelConditionEvaluator.java:70)
07:48:35,379 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureBranchEvaluation(MvelConditionEvaluator.java:113)
07:48:35,379 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureCompleteEvaluation(MvelConditionEvaluator.java:106)
07:48:35,380 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureCompleteEvaluation(MvelConditionEvaluator.java:90)
07:48:35,380 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConditionEvaluator.getAnalyzedCondition(MvelConditionEvaluator.java:82)
07:48:35,380 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConstraint.executeJitting(MvelConstraint.java:214)
07:48:35,380 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConstraint.access$000(MvelConstraint.java:41)
07:48:35,380 ERROR [stderr] (Thread-118) at
org.drools.rule.constraint.MvelConstraint$1.run(MvelConstraint.java:201)
07:48:35,381 ERROR [stderr] (Thread-118) at
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
07:48:35,381 ERROR [stderr] (Thread-118) at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
07:48:35,381 ERROR [stderr] (Thread-118) at
java.lang.Thread.run(Thread.java:662)
07:48:35,381 ERROR [stderr] (Thread-118) Caused by:
java.lang.NullPointerException
07:48:35,381 ERROR [stderr] (Thread-118) at
sun.reflect.GeneratedMethodAccessor163.invoke(Unknown Source)
07:48:35,381 ERROR [stderr] (Thread-118) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
07:48:35,382 ERROR [stderr] (Thread-118) at
java.lang.reflect.Method.invoke(Method.java:597)
07:48:35,382 ERROR [stderr] (Thread-118) at
org.mvel2.optimizers.impl.refl.nodes.GetterAccessor.getValue(GetterAccessor.java:40)
07:48:35,382 ERROR [stderr] (Thread-118) ... 22 more
07:48:35,856 ERROR [stderr] (Thread-132) Exception in thread "Thread-132"
java.lang.RuntimeException: unable to invoke method:
com.billing.domain.context.OrderSideContext.getPrimarySide: target of method
is null
07:48:35,856 ERROR [stderr] (Thread-132) at
org.mvel2.optimizers.impl.refl.nodes.GetterAccessor.getValue(GetterAccessor.java:66)
07:48:35,857 ERROR [stderr] (Thread-132) at
org.mvel2.optimizers.impl.refl.nodes.VariableAccessor.getValue(VariableAccessor.java:37)
07:48:35,857 ERROR [stderr] (Thread-132) at
org.mvel2.ast.ASTNode.getReducedValueAccelerated(ASTNode.java:108)
07:48:35,857 ERROR [stderr] (Thread-132) at
org.mvel2.ast.BinaryOperation.getReducedValueAccelerated(BinaryOperation.java:107)
07:48:35,857 ERROR [stderr] (Thread-132) at
org.mvel2.ast.And.getReducedValueAccelerated(And.java:34)
07:48:35,857 ERROR [stderr] (Thread-132) at
org.mvel2.MVELRuntime.execute(MVELRuntime.java:85)
07:48:35,857 ERROR [stderr] (Thread-132) at
org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:123)
07:48:35,858 ERROR [stderr] (Thread-132) at
org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:116)
07:48:35,858 ERROR [stderr] (Thread-132) at
org.mvel2.MVEL.executeExpression(MVEL.java:930)
07:48:35,858 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConditionEvaluator.evaluate(MvelConditionEvaluator.java:70)
07:48:35,858 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureBranchEvaluation(MvelConditionEvaluator.java:113)
07:48:35,858 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureCompleteEvaluation(MvelConditionEvaluator.java:106)
07:48:35,859 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureCompleteEvaluation(MvelConditionEvaluator.java:90)
07:48:35,859 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConditionEvaluator.getAnalyzedCondition(MvelConditionEvaluator.java:82)
07:48:35,859 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConstraint.executeJitting(MvelConstraint.java:214)
07:48:35,859 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConstraint.access$000(MvelConstraint.java:41)
07:48:35,859 ERROR [stderr] (Thread-132) at
org.drools.rule.constraint.MvelConstraint$1.run(MvelConstraint.java:201)
07:48:35,859 ERROR [stderr] (Thread-132) at
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
07:48:35,860 ERROR [stderr] (Thread-132) at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
07:48:35,860 ERROR [stderr] (Thread-132) at
java.lang.Thread.run(Thread.java:662)
07:48:35,860 ERROR [stderr] (Thread-132) Caused by:
java.lang.NullPointerException
07:48:35,860 ERROR [stderr] (Thread-132) at
sun.reflect.GeneratedMethodAccessor163.invoke(Unknown Source)
07:48:35,860 ERROR [stderr] (Thread-132) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
07:48:35,860 ERROR [stderr] (Thread-132) at
java.lang.reflect.Method.invoke(Method.java:597)
07:48:35,861 ERROR [stderr] (Thread-132) at
org.mvel2.optimizers.impl.refl.nodes.GetterAccessor.getValue(GetterAccessor.java:40)
07:48:35,861 ERROR [stderr] (Thread-132) ... 19 more
07:48:36,914 ERROR [stderr] (Thread-96) Exception in thread "Thread-96"
[Error: null pointer or function not found: cSide]
07:48:36,914 ERROR [stderr] (Thread-96) [Near : {... == "A" ||
liquidityIndicator != "R" && matchId < $tsc.cSide.matchId ....}]
07:48:36,914 ERROR [stderr] (Thread-96)
^
07:48:36,915 ERROR [stderr] (Thread-96) [Line: 1, Column: 69]
07:48:36,915 ERROR [stderr] (Thread-96) at
org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer.getMethod(ReflectiveAccessorOptimizer.java:1022)
07:48:36,915 ERROR [stderr] (Thread-96) at
org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer.getMethod(ReflectiveAccessorOptimizer.java:987)
07:48:36,915 ERROR [stderr] (Thread-96) at
org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer.getBeanProperty(ReflectiveAccessorOptimizer.java:677)
07:48:36,915 ERROR [stderr] (Thread-96) at
org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer.getBeanPropertyAO(ReflectiveAccessorOptimizer.java:472)
07:48:36,916 ERROR [stderr] (Thread-96) at
org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer.compileGetChain(ReflectiveAccessorOptimizer.java:374)
07:48:36,916 ERROR [stderr] (Thread-96) at
org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer.optimizeAccessor(ReflectiveAccessorOptimizer.java:143)
07:48:36,916 ERROR [stderr] (Thread-96) at
org.mvel2.ast.ASTNode.optimize(ASTNode.java:159)
07:48:36,916 ERROR [stderr] (Thread-96) at
org.mvel2.ast.ASTNode.getReducedValueAccelerated(ASTNode.java:115)
07:48:36,916 ERROR [stderr] (Thread-96) at
org.mvel2.ast.BinaryOperation.getReducedValueAccelerated(BinaryOperation.java:107)
07:48:36,916 ERROR [stderr] (Thread-96) at
org.mvel2.ast.And.getReducedValueAccelerated(And.java:34)
07:48:36,917 ERROR [stderr] (Thread-96) at
org.mvel2.MVELRuntime.execute(MVELRuntime.java:85)
07:48:36,917 ERROR [stderr] (Thread-96) at
org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:123)
07:48:36,917 ERROR [stderr] (Thread-96) at
org.mvel2.compiler.CompiledExpression.getValue(CompiledExpression.java:116)
07:48:36,917 ERROR [stderr] (Thread-96) at
org.mvel2.MVEL.executeExpression(MVEL.java:930)
07:48:36,917 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConditionEvaluator.evaluate(MvelConditionEvaluator.java:70)
07:48:36,917 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureBranchEvaluation(MvelConditionEvaluator.java:113)
07:48:36,918 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureCompleteEvaluation(MvelConditionEvaluator.java:106)
07:48:36,918 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConditionEvaluator.ensureCompleteEvaluation(MvelConditionEvaluator.java:90)
07:48:36,918 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConditionEvaluator.getAnalyzedCondition(MvelConditionEvaluator.java:82)
07:48:36,918 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConstraint.executeJitting(MvelConstraint.java:214)
07:48:36,918 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConstraint.access$000(MvelConstraint.java:41)
07:48:36,919 ERROR [stderr] (Thread-96) at
org.drools.rule.constraint.MvelConstraint$1.run(MvelConstraint.java:201)
07:48:36,919 ERROR [stderr] (Thread-96) at
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
07:48:36,919 ERROR [stderr] (Thread-96) at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
07:48:36,919 ERROR [stderr] (Thread-96) at
java.lang.Thread.run(Thread.java:662)
--
View this message in context: http://drools.46999.n3.nabble.com/Drools-5-4-Can-t-catch-Exception-tp4018...
Sent from the Drools: User forum mailing list archive at Nabble.com.
12 years, 4 months