Saga entry table/ Sagas collection remains empty - java

I've been following AxonBank example in order to understand implementation of Saga in Axon framework and have some code like this for starting and ending saga
#Saga
public class MoneyTransferSaga {
#Inject
private transient CommandGateway commandGateway;
private String targetAccount;
private String transferId;
#StartSaga
#SagaEventHandler(associationProperty = "transferId")
public void on(MoneyTransferRequestedEvent event) {
System.out.println("Inside start saga for money transfer event");
targetAccount = event.getTargetAccount();
transferId = event.getTransferId();
SagaLifecycle.associateWith("transactionId", transferId);
System.out.println("## These are the params going into WMC : sourceAccount: " + event.getSourceAccount()
+ " transferID: " + transferId + " event.getAmount: " + event.getAmount());
commandGateway.send(new WithdrawMoneyCommand(event.getSourceAccount(), transferId, event.getAmount()),
new CommandCallback<WithdrawMoneyCommand, Object>() {
#Override
public void onSuccess(CommandMessage<? extends WithdrawMoneyCommand> commandMessage,
Object result) {
}
#Override
public void onFailure(CommandMessage<? extends WithdrawMoneyCommand> commandMessage,
Throwable cause) {
System.out.println("On failure of withdraw money command inside saga ");
System.out.println("###################### Cause of failure = " + cause);
commandGateway.send(new CancelMoneyTransferCommand(event.getTransferId()));
}
});
}
#SagaEventHandler(associationProperty = "transactionId")
public void on(MoneyWithdrawnEvent event) {
System.out.println("Inside saga event handler for monney withdrawnevent");
commandGateway.send(new DepositMoneyCommand(targetAccount, event.getTransactionId(), event.getAmount()),
LoggingCallback.INSTANCE);
}
#SagaEventHandler(associationProperty = "transactionId")
public void on(MoneyDepositedEvent event) {
System.out.println("Inside saga event handler for money deposited event");
commandGateway.send(new CompleteMoneyTransferCommand(transferId), LoggingCallback.INSTANCE);
}
#EndSaga
#SagaEventHandler(associationProperty = "transferId")
public void on(MoneyTransferCompletedEvent event) {
System.out.println("Inside Endsaga for money transfer complete event");
}
#SagaEventHandler(associationProperty = "transferId")
public void on(MoneyTransferCancelledEvent event) {
end();
}
}
After performing the money transfer via the REST API all this code gets executed as I can see my logs getting printed to console and all transactions being stored in account table.
All entries exist in domain_event_entry also, but the saga_entry and association_value_entry tables remain empty regardless of whether the transaction is success or failure.
Initially I thought this might be because of a misconfigured saga store so I configured the saga store with MongoSagaStore but still the collection of Saga remains empty.
So am I missing something here or axon just deletes the data from these tables after the saga is complete?

AxonFramework will automatically remove a Saga entry from its storage, including any associations, when it has ended. So you'll only ever see information of active instances, there.
In the sample application, all bus components use the "Simple..." implementation, which basically means all activities are executed in the same thread. Therefore, once you have received the OK nor NotOK, all activities by the Saga will have ended as well.
If you were to replace them with Async or Distributed implementations, this is no longer the case. You OK will be returned before the entire process has finished.

Related

state processing function not working as expected

I'm working on state processing function where im keying on an ID and would like to hold a state for like 60 secs.
DataStream<String> keyedStream = stream.keyBy(Events::getAnonymousId)
.process(new KeyedProcessingWithCallBack(Long.parseLong(parameters.get("ttl"))))
.uid("keyed-processing");
keyedStream.sinkTo(sink(parameters))
.uid("kafka-sink");
public class KeyedProcessingWithCallBack extends KeyedProcessFunction<String, Events, String> {
ValueState<Boolean> anonymousIdHasBeenSeen;
private final long stateTtl;
public KeyedProcessingWithCallBack(long stateTtl) {
this.stateTtl = stateTtl;
}
#Override
public void open(Configuration parameters) throws Exception {
ValueStateDescriptor<Boolean> desc = new ValueStateDescriptor<>("anonymousIdHasBeenSeen", Types.BOOLEAN);
// defines the time the state has to be stored in the state backend before it is auto cleared
anonymousIdHasBeenSeen = getRuntimeContext().getState(desc);
}
#Override
public void processElement(EngagerEvents value, KeyedProcessFunction<String, Events, String>.Context ctx, Collector<String> out) throws Exception {
if (anonymousIdHasBeenSeen.value() == null) {
System.out.println("Value is NULL : " +value.getAnonymousId());
// key is not available in the state
anonymousIdHasBeenSeen.update(true);
System.out.println("TIMER START TIME: " +ctx.timestamp());
ctx.timerService().registerProcessingTimeTimer(ctx.timestamp() + (stateTtl * 1000));
out.collect(value.getEventString());
}
}
#Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out)
throws Exception {
// triggers after ttl has passed
System.out.println("Call back triggered : time : " +timestamp + " value : " +anonymousIdHasBeenSeen.value());
if (anonymousIdHasBeenSeen.value()) {
anonymousIdHasBeenSeen.clear();
}
}
I have registered a timer to clear value from state. however as per my logs it is triggering correctly. but my process element is accepting the value for the same key before even it is cleared in the call back.
Expect data in the output topic are separated by a minute gap, but is not.
Can someone point out the mistake in my implementation here. I'm spanning multiple threads to pump request at the same time.

How to have one Hibernate session per scheduled method

I have the following setup:
#Component
public class Scheduler {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
BatchService batchService;
#Scheduled(cron = "0 */1 * ? * *")
void tick() {
logger.info("Beginning of a batch tick");
batchService.refundNotAssignedVisits();
logger.info("End of the batch tick");
}
}
With BatchService containing the following:
#Service
public class BatchServiceImpl implements BatchService {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
VisitService visitService;
#Override
#Transactional
public void refundNotAssignedVisits() {
logger.info("Start automatic refund of past visits being assigned");
Set<Visit> visits = visitService.findRefundableVisits();
if(visits != null && visits.size() != 0) {
logger.info("Found " + visits.size() + " visits to refund with IDs: " + visits.stream().map(x -> x.getId().toString()).collect(Collectors.joining(", ")));
visits.forEach(x -> {
logger.info("Refunding visit with ID: " + x.getId());
try {
visitService.cancel(x);
logger.info("Visit successfully refunded!");
}
catch(Exception e) {
logger.error("Error while refunding visit...", e);
}
});
}
else {
logger.info("Found no visit to refund.");
}
logger.info("End of automatic refund");
}
}
And the cancel method defined like this:
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Visit cancel(Visit visit) throws Exception {
// Some business logic
}
I need the cancel method to have one transaction per call, for business purposes, and at the moment, the refundNotAssignedVisits is #Transactional in order to enable a Hibernate session so I am able to use lazy loading with related entities in the cancel method.
This causes problems such as duplicate commits and I'm wondering what's a good pattern to achieve what I want: have a #Scheduled method that enables a Hibernate session in order to make multiple calls to another method with one transaction per call.
#Transactional 's REQUIRES_NEW will create another new Hibernate session , so the session inside cancel() will be different from the session that is used to load the entities which seems like awkward to me. Normally , we use the same session for loading and managing the same entity within a transaction.
I will refactor the codes into the followings :
VisitService:
//Cannel by visitorId and load the Visitor by Id in a new transaction
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Visit cancel(Integer visitorId) throws Exception {
Visit visit= session.get(Visit.class , visitorId);
cancel(visit);
}
#Override
public Visit cancel(Visit visit) throws Exception {
// Some business logic
}
//Add method to return the IDs only
#Transactional(readOnly=true)
public Set<Integer> findRefundableVisitId(){
}
BatchService:
//#Transactional (Do not require anymore)
public void refundNotAssignedVisits() {
logger.info("Start automatic refund of past visits being assigned");
Set<Integer> refundVisitIds = visitService.findRefundableVisitId();
refundVisitIds.forEach( id-> {
try {
visitService.refund(id);
logger.info("Visit successfully refunded!");
}
catch(Exception e) {
logger.error("Error while refunding visit...", e);
}
});
}
In this way , each refund is executed in their own transaction and the transaction that is used to load the refund visitors do not need to wait for all refund complete in order to commit and no more "duplicate commits".

How to access the payload of the message arrived of the callback method (messageArrived) in the main method Eclipse Paho?

Problem statement:- I am trying to automate a MQTT flow, for that I a need to publish and subscribe to multiple topics but in a sequential order. The trick part is that the message received from the first publish has some value which will be passed in the next sub/pub commands.
For eg.
Sub to topicA/abc
Pub to topicA/abc
Message received on topicA/abc is xyz
sub to topic topicA/xyz
pub to topic topicA/xyz
I am able to receive the message on the first topic but I am not getting how to access the payload of the received message in the main method and pass and attach it to the next topic for next sub.
Is there a way to get the retrieved the message payload from messageArrived callback method to the main method where is client instance is created?
Note:- I am using a single client for publish and subscribe.
kindly help me out as I have ran out of options and methods to do so.
Edited:-
Code snippet
Main class
public class MqttOverSSL {
String deviceId;
MqttClient client = null;
public MqttOverSSL() {
}
public MqttOverSSL(String deviceId) throws MqttException, InterruptedException {
this.deviceId = deviceId;
MqttConnection mqttConObj = new MqttConnection();
this.client = mqttConObj.mqttConnection();
}
public void getLinkCodeMethod() throws MqttException, InterruptedException {
client.subscribe("abc/multi/" + deviceId + "/linkcode", 0);
publish(client, "abc/multi/" + deviceId + "/getlinkcode", 0, "".getBytes());
}
}
Mqtt Claback impl:-
public class SimpleMqttCallBack implements MqttCallback {
String arrivedMessage;
#Override
public void connectionLost(Throwable throwable) {
System.out.println("Connection to MQTT broker lost!");
}
#Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
arrivedMessage = mqttMessage.toString();
System.out.println("Message received:\t" + arrivedMessage);
linkCode(arrivedMessage);
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println("Delivery complete callback: Publish Completed "+ Arrays.toString(iMqttDeliveryToken.getTopics()));
}
public void linkCode(String arrivedMessage) throws MqttException {
System.out.println("String is "+ arrivedMessage);
Gson g = new Gson();
GetCode code = g.fromJson(arrivedMessage, GetCode.class);
System.out.println(code.getLinkCode());
}
}
Publisher class:-
public class Publisher {
public static void publish(MqttClient client, String topicName, int qos, byte[] payload) throws MqttException {
String time = new Timestamp(System.currentTimeMillis()).toString();
log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
// Create and configure a message
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
// Send the message to the server, control is not returned until
// it has been delivered to the server meeting the specified
// quality of service.
client.publish(topicName, message);
}
static private void log(String message) {
boolean quietMode = false;
if (!quietMode) {
System.out.println(message);
}
}
}
OK, it's a little clearer what you are trying to do now.
Short answer No, you can not pass values back to the "main method". MQTT is asynchronous that means you have no idea when a message will arrive for a topic you subscribe to.
You need to update your code to deal check what the incoming message topic is and then deal do what ever action you wanted to do with that response in the messageArrived() handler. If you have a sequence of task to do then you may need to implement what is known as a state machine in order to keep track of where you are in the sequence.

H2 Trigger, Notifier not called in Automatic Mixed Mode (AUTO_SERVER=TRUE) on remote client

I'm trying to use H2 Trigger facility to let clients connected to a H2 database in automatic mixed mode (AUTO_SERVER=TRUE) receive notification when something changes in the database table
test(id INTEGER NOT NULL AUTO_INCREMENT, message varchar(1024))
So far only the H2 server receives the TRIGGER notification, while clients cannot receive any notification therefore their only way to check for changes to the database is to poll with queries to the table, but this way the TRIGGER itself is useless, I could just simply all clients and server poll the database for changes!.
Is there some way to let a trigger notify all clients connected or call a method inside each client so that they realize the table has been modified with an insertion (doesn't bother me the delete or update cases)?
I post my code below which is based on this answer by Thomas Mueller (H2 database creator):
import java.sql.*;
import java.util.concurrent.atomic.AtomicLong;
import org.h2.api.Trigger;
public class TestSimpleDb
{
public static void main(String[] args) throws Exception
{
final String url = "jdbc:h2:test;create=true;AUTO_SERVER=TRUE;multi_threaded=true";
boolean isSender = false;
for (String arg : args)
{
if (arg.contains("receiver"))
{
System.out.println("receiver starting");
isSender = false;
}
else if (arg.contains("sender"))
{
System.out.println("sender starting");
isSender = true;
}
}
if (isSender)
{
Connection conn = DriverManager.getConnection(url);
Statement stat = conn.createStatement();
stat.execute("create table test(id INTEGER NOT NULL AUTO_INCREMENT, message varchar(1024))");
stat.execute("create trigger notifier "
+ "before insert, update, delete, rollback "
+ "on test FOR EACH ROW call \""
+ TestSimpleDb.Notifier.class.getName() + "\"");
Thread.sleep(500);
for (int i = 0; i < 10; i++) {
System.out.println("Sender: I change something...");
stat.execute("insert into test(message) values('my message')");
Thread.sleep(1000);
}
conn.close();
}
else
{
new Thread() {
public void run() {
try {
Connection conn = DriverManager.getConnection(url);
while (true) {
;
//this loop is just to keep the thread alive..
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}.start();
}
}
public static class Notifier implements Trigger
{
#Override
public void init(Connection cnctn, String string, String string1, String string2, boolean bln, int i) throws SQLException {
// Initializing trigger
}
#Override
public void fire(Connection conn, Object[] oldRow, Object[] newRow) throws SQLException {
if (newRow != null) {
System.out.println("Received: " + (String) newRow[1]);
}
}
#Override
public void close() {
// ignore
}
#Override
public void remove() {
// ignore
}
}
}
Like all triggers, this trigger is called on the server, that is, when using the automatic mixed mode (like you do) in the process that opened the database first. Therefore, if I first start the "sender", then I get the following output there:
sender starting
Sender: I change something...
Received: my message
Sender: I change something...
Received: my message
and if I then start the "receiver", I get the following messages there:
Receiver: event received
Receiver: event received
Receiver: event received
If you want that the "receiver" can display what rows were changed, you would need a different architecture. For example, you could add a timestamp column to the table (and an index for this column), and then, on the receiver side, query for the rows where the timestamp is new. This will only work for added and changed rows; for removed rows, you might need to add a new table that contains the removed rows since time x. This table would need to be garbage collected from time to time so that it doesn't grow forever.

BlazeDS StreamingAMF: How to detect when flex client closes the connection?

I have a Flex application that connects to a BlazeDS server using the StreamingAMF channel.
On the server-side the logic is handled by a custom adapter that extends ActionScriptAdapter and implements FlexSessionListener and FlexClientListener interfaces.
I am asking how can I detect which "flex-client" has closed a connection when for example the user is closing the browser? (so I can clean some infos inside the database)
I tried using the following:
1. To manually manage the command messages:
#Override
public Object manage(final CommandMessage commandMessage) {
switch (commandMessage.getOperation()) {
case CommandMessage.SUBSCRIBE_OPERATION:
System.out.println("SUBSCRIBE_OPERATION = " + commandMessage.getHeaders());
break;
case CommandMessage.UNSUBSCRIBE_OPERATION:
System.out.println("UNSUBSCRIBE_OPERATION = " + commandMessage.getHeaders());
break;
}
return super.manage(commandMessage);
}
But the clientID's are always different from the ones that came.
2. Listening for sessionDestroyed and clientDestroyed events
#Override
public void clientCreated(final FlexClient client) {
client.addClientDestroyedListener(this);
System.out.println("clientCreated = " + client.getId());
}
#Override
public void clientDestroyed(final FlexClient client) {
System.out.println("clientDestroyed = " + client.getId());
}
#Override
public void sessionCreated(final FlexSession session) {
System.out.println("sessionCreated = " + session.getId());
session.addSessionDestroyedListener(this);
}
#Override
public void sessionDestroyed(final FlexSession session) {
System.out.println("sessionDestroyed = " + session.getId());
}
But those sessionDestroyed and clientDestroyed methods are never called. :(
I made it the following way, by using a custom adapter and a static class with a list of connected clients.
#Override
public Object manage(final CommandMessage commandMessage) {
switch (commandMessage.getOperation()) {
case CommandMessage.SUBSCRIBE_OPERATION:
// add user info
// be aware - each time the selector changes this method is called. So when you add user info check to see if you are not duplicating the clients.
addInfoAboutUser(commandMessage.getHeader("DSId").toString(), commandMessage.getClientId().toString());
break;
case CommandMessage.UNSUBSCRIBE_OPERATION:
clearUserInfo(commandMessage.getClientId().toString());
break;
}
return null;
}
-
Code INFO:
addInfoAboutUser() and clearUserinfo() are private methods in my class that manage the static list of connected clients.
-
NOTE: when a selector is changed from the flex client side the manage() method will be called twice: 1st to unsubscribe and 2nd to subscribe with the new selector.
You need to catch the event onbeforeunload and call a method on server which will cleanup all the client related data. Otherwise there is no way for the Flex client to automatically detect that it is unloaded.
The session should be destroyed when the maximum inactivity interval is exceeded...if the web.xml is properly configured.
Had the same problem as you ... solved it by a "hack" of BlazeDS ... I documented it on my confluence. Perhaps it helps with your problem:
http://dev.c-ware.de/confluence/display/PUBLIC/Litening+for+BlazeDS+client+logins+and+logouts

Categories

Resources