##############################################################################
# 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
#
# AT Crash During Commit
#
# This script automates testing of a specific recovery scenario for the
# JBossTS XTS implementation of the WS-AT 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-AT 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
#
# Client initiates transaction commit
#
# Coordinator initiates prepare of participant P1
# ** Rule system logs dispatch of prepare to P1
# ** Rule system logs receipt of prepared from P1
# Coordinator initiates prepare of participant P2
# ** Rule system logs dispatch of prepare to P2
# ** Rule system logs receipt of prepared from P2
# Coordinator initiates prepare of participant P3
# ** Rule system logs dispatch of prepare to P3
# ** Rule system logs receipt of prepared from P3
#
# Coordinator initiates commit of participant P1
# ** Rule system intercepts commit and crashes JVM
#
# AS reboots
# Recovery system starts after 2 minutes
# Recovery system recreates PREPARED WS-AT 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 commit to P1
# P1 replies with committed
# ** Rule system traces receipt of committed
#
# Coordinator sends commit to P2
# P2 replies with committed
# ** Rule system traces receipt of committed
#
# Coordinator sends commit to P3
# P3 replies with committed
# ** Rule system traces receipt of committed
#
# Coordinator clears log record and completes commit
# ** Rule system detects completed commit 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 commits. 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 for 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 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.at.MultiParticipantPrepareAndCommitTest
# this test invokes a single service registering 3 participants
# 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.at.MultiServicePrepareAndCommitTest
# this test invokes 3 separate services registering a participant with each service
# 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
#
#   prepared received for participant XXXXXX
#   prepared received for participant XXXXXX
#   prepared received for participant XXXXXX
#   JVM exit
#
# n.b. there should be at least one prepared message received for each participant
# and in some cases there may be repeat messages
#
# After reboot the JVM should exit leaving output in the following format in file
# testlog.
#
#   prepared received for participant XXXXXX
#   prepared received for participant XXXXXX
#   prepared received for participant XXXXXX
#   JVM exit
#   committed received for participant XXXXXX
#   committed received for participant XXXXXX
#   committed received for participant 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 coordinator engine
# (CoordinatorEngine) is sent a commit message. It exits the JVM,
# simulating a crash. The trigger location is on entry

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

#######################################################################
# This rule is triggered when a non-recovered coordinator engine
# (CoordinatorEngine) is requested to send a prepare message. It
# traces the call.

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

#######################################################################
# This rule is triggered when a non-recovered coordinator engine
# (CoordinatorEngine) receives a prepared message. It traces the call.

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

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

#######################################################################
# This rule is triggered when a coordinator engine (CoordinatorEngine)
# 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.CoordinatorEngine
METHOD <init>(String, boolean, W3CEndpointReference, boolean, State)
AT ENTRY
BIND identifier = $1,
     recovered=$4
IF recovered
DO debug("created recovered coordinator engine " + identifier),
   trace("log", "created recovered coordinator engine " + identifier)
ENDRULE

#######################################################################
# This rule is triggered when a recovered coordinator engine
# (CoordinatorEngine) is requested to send a commit message. This
# happens during replay of a prepared TX from
# the log. It traces the call.

RULE trace recovered participant commit
CLASS com.arjuna.wst11.messaging.engines.CoordinatorEngine
METHOD commit
AFTER SYNCHRONIZE
BIND engine:CoordinatorEngine = $0,
     recovered:boolean = engine.isRecovered(),
     identifier:String = engine.getId()
IF recovered
DO debug("commit on recovered coordinator engine " + identifier),
   traceln("log", "commit on recovered coordinator engine " + 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 transaction " + uid),
   traceln("log", "replaying commit for prepared transaction " + 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 transaction " + uid),
   debug("removed committed transaction " + uid),
   debug("!!!killing JVM!!!"),
   killJVM()
ENDRULE
