Stateful Session giving major issues
by starfish15
Hello,
I am using Drools 5.2. I am not sure why not able to find the result here.
I am supposed to get the value from the Action in this rule as shown below.
But its never found. Kindly assist me with what is it that i have'nt
included.
First the rules
//generated from Decision Table
import boa.poc.java.SSI;
import boa.poc.java.CMPG_REF;
import boa.poc.java.Result_Rules;
// rule values at C12, header at C7
rule "Access sql table_12"
when
ssi : SSI(CD == "ES", EOC_IN == "Y", IN == "N")
cmpgRef : CMPG_REF(CMPG == 123, 01_NO == "12")
result : Result_Rules(dummyCond == "result")
then
result.setDummyAction("Access I");
end
Second the code
@SuppressWarnings("rawtypes")
public HashMap<String, List> retrievRuleResult(HashMap hm) {
try {
// load up the knowledge base
KnowledgeBase kbase = readKnowledgeBase();
SSI ssi = (SSI)hm.get("pa_cycle_ssi");
CMPG_REF cmpRef = (CMPG_REF)hm.get("pa_cmpg_ref");
Result_Rules rulesResult = (Result_Rules)hm.get("dummyCond");
StatefulKnowledgeSession ksession =
kbase.newStatefulKnowledgeSession();
ksession.insert(ssi);
ksession.insert(cmpRef);
ksession.insert(rulesResult);
ksession.fireAllRules();
ksession.dispose();
System.out.println("Result 1 : "+rulesResult.getDummyAction());
return ;
}
public KnowledgeBase readKnowledgeBase() throws Exception {
KnowledgeBuilder kbuilder =
KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("poc/rules/rules.pkg"),
ResourceType.PKG);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
if (errors.size() > 0) {
for (KnowledgeBuilderError error: errors) {
System.err.println(error);
}
throw new IllegalArgumentException("Could not parse knowledge.");
}
KnowledgeBase kbase = kbuilder.newKnowledgeBase();
return kbase;
}
--
View this message in context: http://drools.46999.n3.nabble.com/Stateful-Session-giving-major-issues-tp...
Sent from the Drools: User forum mailing list archive at Nabble.com.
11 years, 10 months
Drools with DB based rule
by starfish15
Hello,
I need to write a rule in a spreadsheet DT according to the below condition
*Fact1.key1 == Fact3.key2*
where the Facts have been designed on the basis of DB Tables and respective
columns. It is mentioned that these tables would be joined on the basis of
these columns i.e. key1and key2.
and further the Action should be the value of these keys. If the condition
satisfies, the action wud be *key1.getId();*
Let me mention that my DB funda are a little STALE, hence probably am not
able to write this rule.
It doesn't seem to be complex, but m absolutely not sure how should i b
writing the same.
Kindly assist ASAP.
Regards,
starfish
--
View this message in context: http://drools.46999.n3.nabble.com/Drools-with-DB-based-rule-tp4021857.html
Sent from the Drools: User forum mailing list archive at Nabble.com.
11 years, 10 months
Drools locking threads
by nalvesp
I need some support with Drools. I'm using Drools 5.2 in a multi-threaded
environment. Basicaly, every login into a website is calling an execute
method in a StatelessKnowledgeSession. After some time, threads start to
lock as below:
"[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default
(self-tuning)'" id=18 idx=0xc0 tid=7267 prio=5 alive, in native, blocked,
daemon
-- Blocked trying to get lock:
org/drools/event/RuleBaseEventSupport@0x2aab52d6f518[fat lock]
at jrockit/vm/Threads.waitForUnblockSignal()V(Native Method)
at jrockit/vm/Locks.fatLockBlockOrSpin(Locks.java:1679)[optimized]
at jrockit/vm/Locks.lockFat(Locks.java:1780)[optimized]
at
jrockit/vm/Locks.monitorEnterSecondStageHard(Locks.java:1312)[optimized]
at jrockit/vm/Locks.monitorEnterSecondStage(Locks.java:1259)[optimized]
at
org/drools/event/AbstractEventSupport.addEventListener(AbstractEventSupport.java:65)[inlined]
at
org/drools/common/AbstractRuleBase.addEventListener(AbstractRuleBase.java:1018)[optimized]
at
org/drools/impl/KnowledgeBaseImpl.addEventListener(KnowledgeBaseImpl.java:127)[optimized]
at
org/jbpm/process/instance/ProcessRuntimeImpl.initProcessEventListeners(ProcessRuntimeImpl.java:201)
at
org/jbpm/process/instance/ProcessRuntimeImpl.<init>(ProcessRuntimeImpl.java:68)[optimized]
at
org/jbpm/process/instance/ProcessRuntimeFactoryServiceImpl.newProcessRuntime(ProcessRuntimeFactoryServiceImpl.java:10)
at
org/jbpm/process/instance/ProcessRuntimeFactoryServiceImpl.newProcessRuntime(ProcessRuntimeFactoryServiceImpl.java:6)
at
org/drools/runtime/process/ProcessRuntimeFactory.newProcessRuntime(ProcessRuntimeFactory.java:12)
at
org/drools/common/AbstractWorkingMemory.createProcessRuntime(AbstractWorkingMemory.java:359)[inlined]
at
org/drools/common/AbstractWorkingMemory.setKnowledgeRuntime(AbstractWorkingMemory.java:1260)[inlined]
at
org/drools/impl/StatefulKnowledgeSessionImpl.<init>(StatefulKnowledgeSessionImpl.java:116)[inlined]
at
org/drools/impl/StatelessKnowledgeSessionImpl.newWorkingMemory(StatelessKnowledgeSessionImpl.java:129)[inlined]
at
org/drools/impl/StatelessKnowledgeSessionImpl.execute(StatelessKnowledgeSessionImpl.java:293)[optimized]
at
br/com/cielo/engineRules/services/impl/CancelamentoRuleBean.validarRegrasCancelamento(CancelamentoRuleBean.java:79)[optimized]
at
br/com/cielo/engineRules/services/impl/CancelamentoRuleEJB_vyscvn_EOImpl.validarRegrasCancelamento(CancelamentoRuleEJB_vyscvn_EOImpl.java:205)[optimized]
at
br/com/cielo/engineRules/services/impl/CancelamentoRuleEJB_vyscvn_EOImpl_WLSkel.invoke(ILweblogic/rmi/spi/InboundRequest;Lweblogic/rmi/spi/OutboundResponse;Ljava/lang/Object;)Lweblogic/rmi/spi/OutboundResponse;(Unknown
Source)[optimized]
at
weblogic/rmi/internal/BasicServerRef.invoke(BasicServerRef.java:589)[optimized]
at
weblogic/rmi/cluster/ClusterableServerRef.invoke(ClusterableServerRef.java:230)[optimized]
at
weblogic/rmi/internal/BasicServerRef$1.run(BasicServerRef.java:477)[inlined]
at
weblogic/security/acl/internal/AuthenticatedSubject.doAs(AuthenticatedSubject.java:363)[inlined]
at
weblogic/security/service/SecurityManager.runAs(Lweblogic/security/acl/internal/AuthenticatedSubject;Lweblogic/security/acl/internal/AuthenticatedSubject;Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;(Unknown
Source)[inlined]
at
weblogic/rmi/internal/BasicServerRef.handleRequest(BasicServerRef.java:473)[inlined]
at
weblogic/rmi/internal/wls/WLSExecuteRequest.run(WLSExecuteRequest.java:118)[optimized]
at
weblogic/work/ExecuteThread.execute(ExecuteThread.java:201)[optimized]
at weblogic/work/ExecuteThread.run(ExecuteThread.java:173)
at jrockit/vm/RNI.c2java(JJJJJ)V(Native Method)
-- end of trace
I went to Drool's source code and there's a synchronized section right
before the lock. I'm afraid Drools doesn't support multi-thread processing.
Does anyone know anything about this issue???
Tks
--
View this message in context: http://drools.46999.n3.nabble.com/Drools-locking-threads-tp4021732.html
Sent from the Drools: User forum mailing list archive at Nabble.com.
11 years, 10 months
.BRL in Eclipse
by rjr201
Hi,
I'm creating rules within the Guided Editor in Guvnor as this is how my
client wants to view and edit them.
Looking at the documentation here
<http://docs.jboss.org/drools/release/5.5.0.Final/drools-expert-docs/pdf/d...>
it seems to say that I can create and edit .brl files in Eclipse?
I can't seem to work out how to do this. I have what I think is the latest
version of JBoss tools. Does anyone know if this feature has been removed?
It would be very useful to be able to develop the rules in Eclipse rather
than having to create them in Guvnor.
Also, another quick question.. is it possible to convert from .dsl files to
.brl files?
Thanks for any advice that anyone can give.
--
View this message in context: http://drools.46999.n3.nabble.com/BRL-in-Eclipse-tp4021844.html
Sent from the Drools: User forum mailing list archive at Nabble.com.
11 years, 10 months
Re: [rules-users] Why all rule condition fired when insert object in drools session?
by FrankVhh
Hi Zhao,
You could insert a helper fact and use it as a first pattern in your
conditions. That way, the engine will not evaluate too many patterns from
rules that are not to be executed.
When
ActiveGroup(name == "GroupA")
...
Then
...
Implications
* You will have to take care of the status of ActiveGroup and update its
value yourself. Make sure you do it at the appropriate moment. (Low salience
rule?)
* You would have to re-evaluate the need for the agenda-group. Its
functinos are taken over by the fact, do you still need the group?
* Whether it will improve performance or not is a whole different question
all together and is depending on your ruleset. I never did severe testing on
it, but imho, there are certainly cases where the agenda-group is faster
than the fact. As a rule of thumb, I would say that if you do a lot of
update/modify calls in your consequences, you might get an improvement by
blocking the evaluation of patterns early.
Regards,
Frank
PS You could also create a different fact per agenda-group. It should be
mildly faster while providing quite a lot less flexibility.
--
View this message in context: http://drools.46999.n3.nabble.com/Why-all-rule-condition-fired-when-inser...
Sent from the Drools: User forum mailing list archive at Nabble.com.
11 years, 10 months
Re: [rules-users] Question about Drools Spring, StatelessKnowledgeSession and globals
by Patrick van Kann
Hello fellow Droolers,
A while ago I asked why the <drools:batch> element seemed to work
differently in the case of a StatelessKnowledgeSession and a
StatefulKnowedgeSession (see below).
I just wanted to update those interested with the answer and ask some
further questions, probably aimed at any developers who happen to be
reading.
I checked out the 5.1.1.34858 tag and had a look at how the Spring
integration works. I found that the reason that the global is not set in the
case of a StatelessKnowledgeSession is because the <spring:batch> element is
effectively ignored in the StatelessKnowledgeSessionBeanFactory, which lacks
the following code present in the StatefulKnowledgeSessionBeanFactory (in
the internalAfterPropertiesSet() method):
if ( getBatch() != null && !getBatch().isEmpty()) {
for ( Command<?> cmd : getBatch() ) {
ksession.execute( cmd );
}
}
So, either by accident or design the <spring:batch> element has no effect
when using a StatelessKnowledgeSession (but is perfectly allowable in the
XSD/Spring namespace).
So my questions are:
1) Is this the intended behaviour or a bug? On reflection, I am now not sure
the idea of a "batch/script" makes sense for a StatelessKnowledgeSession
since execute() is a one-shot method and any globals set this way would not
be available to later executions, which is what I was looking for.
2) If so, should the XSD be changed to disallow the batch element within a
stateless session (difficult, given that this is determined via the "type"
attribute) or should the documentation simply warn people that the
<spring:batch> element doesn't do anything if you set the type attribute to
stateless (somewhat confusing, I suppose)
3) If not, should the code above be added to the
StatelessKnowledgeSessionBeanFactory.internalAfterPropertiesSet() method?
4) Should there be another way to declare a global that isn't through the
batch element (one that would cause globals to be set via the
setGlobal(String, Object) method rather than using the SetGlobalCommand via
the execute() method.
I am happy to try and contribute something along these lines if I can get
some guidance as to what was actually intended.
Hope this helps and thanks again for Drools.
Patrick
=====================================
Hello,
I've been trying out the Spring integration package in Drools 5.1.1 and it
works really well, but I have run into one issue I can't figure out.
I've defined 2 knowledge sessions from the same knowledge base in the app
context - one stateless, one stateful but otherwise identical. They both
refer to a collaborator defined as a bean in the app context which is to be
used as a global in my rules. This is just an excerpt of my full Spring
context, the kbase definition itself is not an issue.
<bean id="applicantDao" class="com.acme.app.dao.impl.ApplicantDaoImpl" />
<drools:ksession id="statelessKSession" type="stateless"
name="statelessKSession" kbase="kbase">
<drools:batch>
<drools:set-global identifier="applicantDao" ref="applicantDao" />
</drools:batch>
</drools:ksession>
<drools:ksession id="statefulKSession" type="stateful"
name="statefulKSession" kbase="kbase">
<drools:batch>
<drools:set-global identifier="applicantDao" ref="applicantDao" />
</drools:batch>
</drools:ksession>
The issue is that this configuration works for the stateful but not the
stateless session, in the sense that the stateful session appears to have a
valid reference to the applicantDao object in the Globals object but the
stateless session doesn't.
<at> Test
public void testStatelessGlobal() {
Globals globals = statelessKSession.getGlobals();
Object global = globals.get("applicantDao");
Assert.assertNotNull(global);
}
<at> Test
public void testStatefulGlobal() {
Globals globals = statefulKSession.getGlobals();
Object global = globals.get("applicantDao");
Assert.assertNotNull(global);
}
The first test fails (the global variable is null) but the second passes. No
errors are thrown by Drools during the setup of the Spring container.
What am I doing wrong? Should I be able to define globals in this way for
stateless sessions? The XSD seems to indicate this is a valid configuration,
but it just doesn't work.
Is anyone else working with the Spring integration that can point out my
error here?
Many thanks,
Patrick
11 years, 10 months
Drools Guvnor Repository Reports
by Aseem Belsare
Hi,
I wish to obtain the version history from Guvnor repository. Stumbled upon
http://drools.46999.n3.nabble.com/Guvnor-Repository-Report-td3376667.html...
seems to be related to the same topic, but the link in there is not
working which points to the documentation for using REST API to get the
assets/package version history info.
Is there a way of doing it? I started working on Guvnor just recently and
I've setup a simple rule and making changes to it to be saved in the Guvnor
repo. I wish to pull the version history and generate a report using JAVA
based off of it.
Any help would be appreciated.
Thanks,
Aseem
11 years, 10 months
Planner: The entity ... was never added to this ScoreDirector
by Michiel Vermandel
Hi,
Since I am using my custom move implementation I get:
Exception in thread "main" java.lang.IllegalArgumentException: The entity ({T= Inspect B747-AX1.I#0 [2->2] Andy TL}) was never added to this ScoreDirector.
at org.drools.planner.core.score.director.drools.DroolsScoreDirector.afterVariableChanged(DroolsScoreDirector.java:125)
at my.planner.solver.ProjectMove.moveTask(ProjectMove.java:182)
at my.planner.solver.ProjectMove.doMove(ProjectMove.java:125)
at org.drools.planner.core.localsearch.decider.DefaultDecider.doMove(DefaultDecider.java:143)
at org.drools.planner.core.localsearch.decider.DefaultDecider.decideNextStep(DefaultDecider.java:116)
at org.drools.planner.core.localsearch.DefaultLocalSearchSolverPhase.solve(DefaultLocalSearchSolverPhase.java:62)
at org.drools.planner.core.solver.DefaultSolver.runSolverPhases(DefaultSolver.java:190)
at org.drools.planner.core.solver.DefaultSolver.solve(DefaultSolver.java:155)
at my.planner.app.InspectionSchedule.solve(InspectionSchedule.java:188)
at my.planner.app.InspectionPlanning.main(InspectionPlanning.java:27)
It happens during LocalSearch Phase in my custom move implementation.
I caught this exception and printed the hashcode of the entity.
I also printed the hashcodes of all entities in my cloneSolution method and they match.
Furthermore when catching the exception I loop through the
((InspectionSchedule)scoreDirector.getWorkingSolution()).getTasks() array and again I find the task with that hashcode.
It is ((InspectionSchedule)scoreDirector.getWorkingSolution()).getTasks() that holds the tasks and that is populated when cloning the solution.
It does not happen in every test... only in some, but then it's consistently.
Are there any other things I can check to see where I'm going bad?
Thanks
Michiel
-----------------
http://www.codessentials.com - Your essential software, for free!
Follow us at http://twitter.com/#!/Codessentials
11 years, 10 months
Planner: duplicate moves within a single step?
by Michiel Vermandel
Hi,
I noticed something really strange in my trace logging: certain moves are made multiple times, even consecutive.
How is this possible?
I am using Planner 5.5.0.Final and no custom move factories (see config below).
TRACE: [2013-01-25 14:12:28,309] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (12), score (-515hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => (P4) ).
TRACE: [2013-01-25 14:12:28,309] main org.drools.planner.core.localsearch.decider.DefaultDecider - Ignoring not doable move ({T= Inspect A380-TR1.I#0 [5->6] Kristof IV} => IV).
TRACE: [2013-01-25 14:12:28,310] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (14), score (-2090hard/0soft), accepted (true) for move ({T= Inspect A380-TR1.I#1 [5->6] Gerald IV} => (P9) ).
TRACE: [2013-01-25 14:12:28,310] main org.drools.planner.core.localsearch.decider.DefaultDecider - Ignoring not doable move ({T= Inspect A320-CZ4.I#0 [7->9] Kristof IV} => IV).
TRACE: [2013-01-25 14:12:28,310] main org.drools.planner.core.localsearch.decider.DefaultDecider - Ignoring not doable move ({T= Inspect A380-TR1.I#1 [5->6] Gerald IV} => IV).
TRACE: [2013-01-25 14:12:28,311] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (17), score (-230hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => Andy).
TRACE: [2013-01-25 14:12:28,312] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (18), score (-120hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#0 [5->5] Andy IV} => (P7) ).
TRACE: [2013-01-25 14:12:28,313] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (19), score (-120hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#0 [5->5] Andy IV} => (P7) ).
TRACE: [2013-01-25 14:12:28,313] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (20), score (-30hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => TL).
TRACE: [2013-01-25 14:12:28,315] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (21), score (-2180hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#0 [5->5] Andy IV} => (P1) ).
TRACE: [2013-01-25 14:12:28,316] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (22), score (-515hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => (P4) ).
TRACE: [2013-01-25 14:12:28,317] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (23), score (-515hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => (P4) ).
TRACE: [2013-01-25 14:12:28,318] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (24), score (-3180hard/0soft), accepted (true) for move ({T= Inspect A320-CZ4.I#1 [7->8] Andy IV} => (P1) ).
TRACE: [2013-01-25 14:12:28,318] main org.drools.planner.core.localsearch.decider.DefaultDecider - Ignoring not doable move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => IV).
TRACE: [2013-01-25 14:12:28,320] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (26), score (-120hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => (P5) ).
TRACE: [2013-01-25 14:12:28,322] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (27), score (-45hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => (P8) ).
TRACE: [2013-01-25 14:12:28,323] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (28), score (-30hard/0soft), accepted (true) for move ({T= Inspect A380-TR1.I#0 [5->6] Kristof IV} => TL).
TRACE: [2013-01-25 14:12:28,324] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (29), score (-2635hard/0soft), accepted (true) for move ({T= Inspect A320-CZ4.I#1 [7->8] Andy IV} => (P2) ).
TRACE: [2013-01-25 14:12:28,325] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (30), score (-650hard/0soft), accepted (true) for move ({T= Inspect A320-CZ4.I#0 [7->9] Kristof IV} => (P6) ).
TRACE: [2013-01-25 14:12:28,334] main org.drools.planner.core.localsearch.decider.DefaultDecider - Ignoring not doable move ({T= Inspect A380-TR1.I#2 [5->6] Jesse IV} => Jesse).
TRACE: [2013-01-25 14:12:28,335] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (32), score (-230hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#0 [5->5] Andy IV} => Gerald).
TRACE: [2013-01-25 14:12:28,336] main org.drools.planner.core.localsearch.decider.DefaultDecider - Move index (33), score (-515hard/0soft), accepted (true) for move ({T= Inspect B747-AX1.I#1 [7->7] Jesse IV} => (P4) ).
Config:
<?xml version="1.0" encoding="UTF-8"?>
<solver>
<environmentMode>DEBUG</environmentMode>
<!-- Domain model configuration -->
<solutionClass>be.axi.planner.app.InspectionSchedule</solutionClass>
<planningEntityClass>be.axi.planner.domain.Task</planningEntityClass>
<!-- Score configuration -->
<scoreDirectorFactory>
<scoreDefinitionType>HARD_AND_SOFT</scoreDefinitionType>
<scoreDrl>/inspectionRules.drl</scoreDrl>
</scoreDirectorFactory>
<constructionHeuristic>
<constructionHeuristicType>BEST_FIT_DECREASING</constructionHeuristicType>
<constructionHeuristicPickEarlyType>FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING</constructionHeuristicPickEarlyType>
</constructionHeuristic>
<!--customSolverPhase>
<customSolverPhaseCommandClass>be.axi.planner.solution.SolutionInitializer</customSolverPhaseCommandClass>
</customSolverPhase-->
<localSearch>
<termination>
<terminationCompositionStyle>OR</terminationCompositionStyle>
<maximumSecondsSpend>3600</maximumSecondsSpend>
<scoreAttained>0hard/0soft</scoreAttained>
</termination>
<unionMoveSelector>
<changeMoveSelector>
<valueSelector>
<planningVariableName>type</planningVariableName>
</valueSelector>
</changeMoveSelector>
<changeMoveSelector>
<valueSelector>
<planningVariableName>spector</planningVariableName>
</valueSelector>
</changeMoveSelector>
<changeMoveSelector>
<valueSelector>
<planningVariableName>period</planningVariableName>
</valueSelector>
</changeMoveSelector>
</unionMoveSelector>
<acceptor>
<planningEntityTabuSize>7</planningEntityTabuSize>
</acceptor>
<forager>
<minimalAcceptedSelection>1000</minimalAcceptedSelection>
</forager>
</localSearch>
</solver>
Thanks,
Michiel
-----------------
http://www.codessentials.com - Your essential software, for free!
Follow us at http://twitter.com/#!/Codessentials
11 years, 10 months