##############################################################################
# JBoss, Home of Professional Open Source
# Copyright 2009, Red Hat Middleware LLC, and individual contributors
# by the @authors tag. See the copyright.txt in the distribution for a
# full listing of individual contributors.
#
# This is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this software; if not, write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
#
# @authors Andrew Dinn
#
# BA Crash During Commit
#
# This script automates testing of a specific recovery scenario for the
# JBossTS XTS implementation of the WS-BA 1.1 protocol using orchestration
# rules. The basic scenario employs a coordinator and 3 web services
# running in a single JVM but other variants are possible (see below). The
# scenario is as follows (** indicates intercession by a TOAST rule):
#
# AS boots
# Cient starts a WS-BA activity
# Client invokes web service 1
# Web service 1 registers participant P1
# Client invokes web service 2
# Web service 2 registers participant P2
# Client invokes web service 3
# Web service 3 registers participant P3
#
# Alternative A (assumes participant completion participants)
# Client invokes web service 1 with command P1 complete
# Web service sends completed for participant 1
# ** Rule system traces receipt of completed
# Client invokes web service 2 with command P2 complete
# Web service sends completed for participant 2
# ** Rule system traces receipt of completed
# Client invokes web service 3 with command P3 complete
# Web service sends completed for participant 3
# ** Rule system traces receipt of completed
# End Alternative A
#
# Alternative B
# Client invokes web service 1 with command P1 exit
# Web service sends exit for participant 1
# ** Rule system traces receipt of exit
# Coordinator sends exited for participant 1
# ** Rule system traces send of exited
# <possibly repeat for other participants>
# End Alternative B
# Client closes WS-BA activity
#
# Alternative C (assumes coordinator completion participants)
# Coordinator initiates complete of participant P1
# P1 replies with completed
# ** Rule system traces receipt of completed
# Coordinator initiates complete of participant P2
# P2 replies with completed
# ** Rule system traces receipt of completed
# Coordinator initiates complete of participant P3
# P3 replies with completed
# ** Rule system traces receipt of completed
# End Alternative C
# Coordinator initiates close of participant P1
# ** Rule system intercepts close and crashes JVM
#
# AS reboots
# Recovery system starts after 2 minutes
# Recovery system recreates PREPARED WS-BA activity coordinator
# ** Rule system traces create
# Recovery system recreates participant stub for P1
# ** Rule system traces create
# Recovery system recreates participant stub for P2
# ** Rule system traces create
# Recovery system recreates participant stub for P3
# ** Rule system traces create
# Recovery system calls replay of PREPARED transaction
# ** Rule system traces PREPARED replay invocation
#
# Coordinator sends close to P1
# P1 replies with closed
# ** Rule system traces receipt of closed
#
# Coordinator sends close to P2
# P2 replies with closed
# ** Rule system traces receipt of closed
#
# Coordinator sends close to P3
# P3 replies with closed
# ** Rule system traces receipt of closed
#
# Coordinator clears heuristic log record and completes close of activity
# ** Rule system detects completed activity close and kills JVM
#
# Use of this script
#
# The default way of exercising this test is to deploy the xtstest war
# to a single AS and configure it to run the relevant XTS Service Test.
# The web services and coordinator will be located with the client.
# The number of participants can actually be 2, 3 or more. The
# web service(s), client (i.e. the XTS Service Test which drives
# the test) and coordinator service can optionally be located in separate
# ASs. It is also possible to use just a single web service and have the
# client register multiple participants. The coordinator AS should crash
# when the client closes. At reboot the rest of the test should run
# automatically and the server should be killed after a the heuristic
# transaction is successfuly replayed.
#
# n.b. this test is not appropriate foir the case where only one participant
# is registered since the coordinator employs the one phase optimization,
# omitting to log the activity. In this case the lone participant should be
# automatically compensated at restart.
#
# This script allows for registration of either participant completion
# or coordinator completion participants (alternatives A or C). It is
# legitimate to mix the different types. However, if participant completion
# participants are used then the client must script the participants
# to perform a complete. It is also possible to script an exit for
# either type of participant (alternative B). In this case the completed
# message should not be traced.
#
# This script needs to be passed to a TOAST agent in the JVM running
# the coordinator service both at first boot and at reboot. Output will be
# written to file testlog in the working directory of the AS. n.b. the rules in
# this script only refer to code executed by the coordinator. If the client
# (the selected XTS Service Test) or the web services are located in another
# AS/JVM then the other JVM does not require a TOAST agent or script.
#
# XTS Service tests which can operate with this scenario can be selected for
# execution at AS boot by the XTSServiceTestRunnerBean by setting system
# property
#    org.jboss.jbossts.xts.servicetests.XTSServiceTestName
# to the name of a class which will execute the test. This property must
# be defined in the JVM running the AS to which the xtstest war is deployed
# i.e. the client AS. n.b. if the client is colocated with the coordinator then
# this property must be left undefined at AS reboot otherwise the client
# will run again, starting a new TX which may interfere with recovery of the
# crashed TX.
#
# Available tests include:
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiCoordinatorCompletionParticipantCloseTest
# this test invokes a single service registering 3 coordinator completion participants
# and then closing the activity
# the location of service is defined by defining a system property:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
# if this is not set the value used defaults to
#    http://localhost:8080/xtstest/xtsservicetest1
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiParticipantCompletionParticipantCloseTest
# this test invokes a single service registering 3 participant completion participants
# it sends completed to each of the 3 participants before closing the activity
# the location of service is defined by defining a system property:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
# if this is not set the value used defaults to
#    http://localhost:8080/xtstest/xtsservicetest1
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiCoordinatorCompletionParticipantExitAndCloseTest
# this test invokes a single service registering 3 coordinator completion participants
# it sends an exit to the first participant before closing the activity
# the location of service is defined by defining a system property:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
# if this is not set the value used defaults to
#    http://localhost:8080/xtstest/xtsservicetest1
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiParticipantCompletionParticipantExitAndCloseTest
# this test invokes a single service registering 3 participant completion participants
# it sends an exit to the first participant and completed to the other participants
# before closing the activity
# the location of service is defined by defining a system property:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
# if this is not set the value used defaults to
#    http://localhost:8080/xtstest/xtsservicetest1
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiServiceCoordinatorCompletionParticipantCloseTest
# this test invokes 3 separate services registering a coordinator completion participant with each service
# and then closing the activity
# the location of service is defined by defining by system properties:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
#    org.jboss.jbossts.xts.servicetests.ServiceURL2
#    org.jboss.jbossts.xts.servicetests.ServiceURL3
# if these are not set the values used default to
#    http://localhost:8080/xtstest/xtsservicetest1
#    http://localhost:8080/xtstest/xtsservicetest2
#    http://localhost:8080/xtstest/xtsservicetest3
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiServiceParticipantCompletionParticipantCloseTest
# this test invokes 3 separate services registering a participant completion participant with each service
# it sends completed to each of the 3 participants before closing the activity
# the location of service is defined by defining by system properties:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
#    org.jboss.jbossts.xts.servicetests.ServiceURL2
#    org.jboss.jbossts.xts.servicetests.ServiceURL3
# if these are not set the values used default to
#    http://localhost:8080/xtstest/xtsservicetest1
#    http://localhost:8080/xtstest/xtsservicetest2
#    http://localhost:8080/xtstest/xtsservicetest3
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiServiceCoordinatorCompletionParticipantCloseAndExitTest
# this test invokes 3 separate services registering a coordinator completion participant with each service
# it sends an exit to the first participant before closing the activity
# the location of service is defined by defining by system properties:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
#    org.jboss.jbossts.xts.servicetests.ServiceURL2
#    org.jboss.jbossts.xts.servicetests.ServiceURL3
# if these are not set the values used default to
#    http://localhost:8080/xtstest/xtsservicetest1
#    http://localhost:8080/xtstest/xtsservicetest2
#    http://localhost:8080/xtstest/xtsservicetest3
#
# org.jboss.jbossts.xts.servicetests.test.ba.MultiServiceParticipantCompletionParticipantCloseAndExitTest
# this test invokes 3 separate services registering a participant completion participant with each service
# it sends an exit to the first participant and completed to the other participants
# before closing the activity
# the location of service is defined by defining by system properties:
#    org.jboss.jbossts.xts.servicetests.ServiceURL1
#    org.jboss.jbossts.xts.servicetests.ServiceURL2
#    org.jboss.jbossts.xts.servicetests.ServiceURL3
# if these are not set the values used default to
#    http://localhost:8080/xtstest/xtsservicetest1
#    http://localhost:8080/xtstest/xtsservicetest2
#    http://localhost:8080/xtstest/xtsservicetest3
#
# If the client is run in a different AS to the coordinator then the client
# AS needs to be pointed at the coordinator AS. The easiest
# way to do this is to define the system property
#
#    org.jboss.jbossts.xts11.coordinatorURL
#
# to something like
#
#    http://foo.bar.org:8080/ws-c11/soap/ActivationCoordinator
#
# or alternatively to redefine one or more of the component properties
#
#    org.jboss.jbossts.xts11.coordinator.host
#    org.jboss.jbossts.xts11.coordinator.port
#    org.jboss.jbossts.xts11.coordinator.path
#
# (you probably only need to reset the host component)
#
# Expected output
#
# After the first boot the JVM should exit leaving the following in file testlog
#
#   received exit message for XXXXXXX completion participant stub XXXXXX       (only for CloseAndExit tests)
#   received completed message for XXXXXXX completion participant stub XXXXXX  (number of occcurences depends upon test)
#   received completed message for XXXXXXX completion participant stub XXXXXX
#   received completed message for XXXXXXX completion participant stub XXXXXX
#   JVM exit
#
# After reboot the JVM should exit leaving output in the following format in file
# testlog.
#
#   received exit message for XXXXXXX completion participant stub XXXXXX       (only for CloseAndExit tests)
#   received completed message for XXXXXXX completion participant stub XXXXXX
#   received completed message for XXXXXXX completion participant stub XXXXXX
#   received completed message for XXXXXXX completion participant stub XXXXXX
#   JVM exit
#   created recovered XXXXXXX completion participant stub XXXXXX
#   created recovered XXXXXXX completion participant stub XXXXXX
#   created recovered XXXXXXX completion participant stub XXXXXX
#   replaying close for prepared activity XXXXXX
#   close on recovered XXXXXXX completion participant engine XXXXXX
#   received closed message for XXXXXXX completion participant stub XXXXXX
#   close on recovered XXXXXXX completion participant engine XXXXXX
#   received closed message for XXXXXXX completion participant stub XXXXXX
#   close on recovered XXXXXXX completion participant engine XXXXXX
#   received closed message for XXXXXXX completion participant stub XXXXXX
#   removed closed activity XXXXXX
#
#######################################################################
# This rule opens a file for the trace output during XTS startup
# It will be opened for append at reboot so messages from both runs
# will go to this file
#
RULE open trace file
CLASS org.jboss.jbossts.XTSService
METHOD start()
BIND NOTHING
IF TRUE
DO openTrace("log", "testlog")
ENDRULE

#######################################################################
## rules for first run of AS

#######################################################################
# This rule is triggered when a non-recovered participant completion
# participant stub (ParticipantCompletionCoordinatorEngine) is sent
# a close message. It exits the JVM, simulating a crash.
# The trigger location is on entry

RULE kill JVM at participant completion close
CLASS com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine
METHOD close
AT ENTRY
BIND engine:ParticipantCompletionCoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF (NOT recovered)
   AND
   debug("close on non-recovered participant completion engine " + identifier)
DO traceln("log", "JVM exit"),
   debug("!!!killing JVM!!!"),
   killJVM()
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered coordinator completion
# participant stub (CoordinatorCompletionCoordinatorEngine) is sent
# a close message. It exits the JVM, simulating a crash.
# The trigger location is on entry

RULE kill JVM at participant completion close
CLASS com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine
METHOD close
AT ENTRY
BIND engine:CoordinatorCompletionCoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF (NOT recovered)
DO traceln("log", "JVM exit"),
   debug("close on non-recovered coordinator completion engine " + identifier),
   debug("!!!killing JVM!!!"),
   killJVM()
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered coordinator completion
# participant stub (CoordinatorCompletionCoordinatorEngine) is requested
# to send a complete message. It traces the call.

RULE trace coordinator completion close
CLASS com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine
METHOD complete
AFTER SYNCHRONIZE
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF NOT recovered
DO debug("complete on recovered coordinator completion participant engine " + identifier),
   traceln("log", "complete on recovered coordinator completion participant engine " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered participant completion
# participant stub (ParticipantCompletionCoordinatorEngine) receives a
# completed message. It traces the call.

RULE trace participant completion completed
CLASS com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine
METHOD completed(NotificationType, AddressingProperties, ArjunaContext)
AT ENTRY
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF NOT recovered
DO debug("received completed message for participant completion participant stub " + identifier),
   traceln("log", "received completed message for participant completion participant stub " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered coordinator completion
# participant stub (CoordinatorCompletionCoordinatorEngine) receives a
# completed message. It traces the call.

RULE trace coordinator completion completed
CLASS com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine
METHOD completed(NotificationType, AddressingProperties, ArjunaContext)
AT ENTRY
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF NOT recovered
DO debug("received completed message for coordinator completion participant stub " + identifier),
   traceln("log", "received completed message for coordinator completion participant stub " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered participant completion
# participant stub (ParticipantCompletionCoordinatorEngine) receives an
# exit message. It traces the call.

RULE trace participant completion exit
CLASS com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine
METHOD exit(NotificationType, AddressingProperties, ArjunaContext)
AT ENTRY
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF NOT recovered
DO debug("received exit message for participant completion participant stub " + identifier),
   traceln("log", "received exit message for participant completion participant stub " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered coordinator completion
# participant stub (CoordinatorCompletionCoordinatorEngine) receives an
# exit message. It traces the call.

RULE trace coordinator completion exit
CLASS com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine
METHOD exit(NotificationType, AddressingProperties, ArjunaContext)
AT ENTRY
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF NOT recovered
DO debug("received exit message for coordinator completion participant stub " + identifier),
   traceln("log", "received exit message for coordinator completion participant stub " + identifier)
ENDRULE

#######################################################################
## rules for reboot run of AS

#######################################################################
# This rule is triggered when a participant completion participant stub
# (ParticipantCompletionCoordinatorEngine) is created from details
# located in the log record. It traces the create operation
# The trigger location is at entry but the rule should only be triggered
# after calling the super constructor
RULE trace participant completion engine create
CLASS com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine
METHOD <init>(String, boolean, W3CEndpointReference, State, boolean)
AT ENTRY
BIND identifier = $1,
     recovered=$4
IF recovered
DO debug("created recovered participant completion participant stub " + identifier),
   trace("log", "created recovered participant completion participant stub " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a coordinator completion participant stub
# (CoordinatorCompletionCoordinatorEngine) is created from details
# located in the log record. It traces the create operation
# The trigger location is at entry but the rule should only be triggered
# after calling the super constructor
RULE trace coordinator completion engine create
CLASS com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine
METHOD <init>(String, boolean, W3CEndpointReference, State, boolean)
AT ENTRY
BIND identifier = $1,
     recovered=$4
IF recovered
DO debug("created recovered coordinator completion participant stub " + identifier),
   trace("log", "created recovered coordinator completion participant stub " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a recovered participant completion
# participant stub (ParticipantCompletionCoordinatorEngine) is requested
# to send a close message. This happens during replay of a prepared TX from
# the log. It traces the call.

RULE trace participant completion close
CLASS com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine
METHOD close
AFTER SYNCHRONIZE
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF recovered
DO debug("close on recovered participant completion participant engine " + identifier),
   traceln("log", "close on recovered participant completion participant engine " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a recovered coordinator completion
# participant stub (CoordinatorCompletionCoordinatorEngine) is requested
# to send a close message. This happens during replay of a prepared TX from
# the log. It traces the call.

RULE trace coordinator completion close
CLASS com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine
METHOD close
AFTER SYNCHRONIZE
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF recovered
DO debug("close on recovered coordinator completion participant engine " + identifier),
   traceln("log", "close on recovered coordinator completion participant engine " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered participant completion
# participant stub (ParticipantCompletionCoordinatorEngine) receives a
# closed message. It traces the call.

RULE trace participant completion closed
CLASS com.arjuna.wst11.messaging.engines.ParticipantCompletionCoordinatorEngine
METHOD closed(NotificationType, AddressingProperties, ArjunaContext)
AT ENTRY
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF NOT recovered
DO debug("received closed message for participant completion participant stub " + identifier),
   traceln("log", "received closed message for participant completion participant stub " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a non-recovered coordinator completion
# participant stub (CoordinatorCompletionCoordinatorEngine) receives a
# closed message. It traces the call.

RULE trace coordinator completion closed
CLASS com.arjuna.wst11.messaging.engines.CoordinatorCompletionCoordinatorEngine
METHOD closed(NotificationType, AddressingProperties, ArjunaContext)
AT ENTRY
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF NOT recovered
DO debug("received closed message for coordinator completion participant stub " + identifier),
   traceln("log", "received closed message for coordinator completion participant stub " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when the recovery system finds a PREPARED
# activity in the log and reruns the phase 2 commit operation.
# It prints a message which can be used to verify that the test has
# progressed as expected.

RULE trace prepared replay
CLASS org.jboss.jbossts.xts.recovery.coordinator.ba.RecoverACCoordinator
METHOD replayPhase2
AT INVOKE phase2Commit
BIND coordinator = $0,
     uid : Uid = coordinator.identifier(),
     status : int = coordinator.status()
IF (status == com.arjuna.ats.arjuna.coordinator.ActionStatus.PREPARED)
     OR
     (status == com.arjuna.ats.arjuna.coordinator.ActionStatus.COMMITTING)
DO debug("replaying close for prepared activity " + uid),
   traceln("log", "replaying close for prepared activity " + uid)
ENDRULE

#######################################################################
# This rule is triggered when the recovery system deletes the COMMITTED
# activity from the log. It prints a message which can be used to
# verify that the test has completed. As a convenience itt also kills
# the JVM to halt the test.

RULE trace remove committed state
CLASS com.arjuna.ats.arjuna.coordinator.BasicAction
METHOD updateState
AFTER CALL remove_committed
BIND action : BasicAction = $0,
     uid  = action.get_uid()
IF TRUE
DO traceln("log", "removed committed activity " + uid),
   debug("removed committed transaction " + uid),
   debug("!!!killing JVM!!!"),
   killJVM()
ENDRULE
