I just started with Lagom & Akka. I am following the design decribed in Domain Modelling with Akka Persistence Typed
I am trying to create a brand new instance of an entity (EntityState). But the event is not getting persisted, and I am getting the following error:
00:54:27.862 [error] com.example.impl.entity.EntityClass [persistencePhase=running-cmd, akkaAddress=akka://XXX#127.0.0.1:60685, akkaSource=akka://XXX/system/sharding/StateClass/186/ID1, sourceActorSystem=XXX, persistenceId=StateClass|ID1] - Supervisor StopSupervisor saw failure: null
java.lang.NullPointerException: null
at akka.persistence.typed.javadsl.EventSourcedBehavior.$anonfun$apply$4(EventSourcedBehavior.scala:195)
at akka.persistence.typed.internal.Running$RunningState.applyEvent(Running.scala:78)
at akka.persistence.typed.internal.Running$HandlingCommands.applyEffects(Running.scala:153)
at akka.persistence.typed.internal.Running$HandlingCommands.onCommand(Running.scala:123)
at akka.persistence.typed.internal.Running$HandlingCommands.onMessage(Running.scala:105)
at akka.persistence.typed.internal.Running$HandlingCommands.onMessage(Running.scala:100)
at akka.actor.typed.scaladsl.AbstractBehavior.receive(AbstractBehavior.scala:83)
I have a Create command, which invokes onCreate(), and eventually attempts to persist an EntityCreated event.
Service Impl method
#Override
public ServiceCall<CreateMessage, StateView> createState(){
return message ->
entityRef(message.getName())
.<EntityClass.Accepted>ask(replyTo -> new EntityClass.Create(message, replyTo), askTimeout)
.thenApply(accepted -> toStateView(accepted.getSummary()));
}
Command handler:
private ReplyEffect<Event, StateClass> onCreate(StateClass state, Create cmd) {
return Effect()
.persist(new EntityCreated(cmd.getDetails().getName(), Instant.now()))
.thenReply(cmd.replyTo, e -> new Accepted(EntityClass.toSummary(e)));
}
I am able to confirm the following:
exception is thrown during persist()
the event is not present in Cassandra
Your help is appreciated. Thank you in advance!
It seems that the real cause for the exception was because I should have added logic for handling the event as follows:
in helloEvents(), I needed to add logic similar to the following:
if (eventAndOffset.first() instanceof HelloEvent.GreetingMessageChanged) {
HelloEvent.GreetingMessageChanged messageChanged = (HelloEvent.GreetingMessageChanged) eventAndOffset.first();
eventToPublish = new GreetingMessageChanged(messageChanged.getName(), messageChanged.getMessage());
}
In Addition, in the aggregate's eventHandler(), I needed to add logic similar to the following:
builder.forAnyState()
.onEvent(GreetingMessageChanged.class, (state, evt) ->
// We simply update the current state to use the greeting message from
// the event.
state.withMessage(evt.message)
);
Related
I'm having some trouble with reactive streams and based on my code below, the return value of the function should be the value created by the call to myServiceConnector.createSummary(request), whether or not the following call to otherService.postDetails(summary, details, item) throws an exception or not. If it throws an exception I just want to do some logging and otherwise ignore it.
public Mono<Summary> createSummary(final Details details, String authorisation)
{
return myService.doSomething(details.getItemId(), authorisation)
.zipWhen(item -> Mono.just(convertToMyRequest(item, details, myServiceConfig.getBaseRedirectUrl())))
.flatMap(tuple -> {
MyItem item = tuple.getT1();
OrderRequest request = tuple.getT2();
return myServiceConnector.createSummary(request)
.doOnSuccess(summary -> otherService.postDetails(summary, details, item)
.onErrorContinue((o,i) -> {
// Log error
}));
});
}
Currently it seems that the onErrorContinue call is not called (I am forcing an exception to be thrown by otherService.postDetails(summary, details, item) in my test). I have also tried onErrorResume which was called but the exception was still thrown so I got no Summary object returned. Not sure if I have my error handling in the right place.
Updating to include test code below:
#Test
public void returnSummaryWhenOtherServiceFails()
{
Details details = Details.builder()
.itemId(ITEM_ID)
.build();
when(myServiceConfig.getBaseRedirectUrl()).thenReturn(BASE_REDIRECT_URL);
when(myService.doSomething(ITEM_ID, AUTH_STRING)).thenReturn(Mono.just(ITEM));
when(myServiceConnector.createSummary(any())).thenReturn(SUMMARY);
when(otherService.postDetails(any(), any(), any())).thenThrow(WebClientResponseException.class);
summaryService.createSummary(details, AUTH_STRING).block();
verify(myServiceConnector).createSummary(any());
}
The test fails due to:
org.springframework.web.reactive.function.client.WebClientResponseException
If you want to call otherService.postDetails in background and don't care about its result then you can do it like this:
otherService.postDetails(...).subscribe()
or
otherService.postDetails(...).publishOn(Schedulers.elastic()).subscribe()
it's depended on your code.
Or you can change your code like this:
myServiceConnector.createSummary(request)
.flatMap(summary -> otherService.postDetails(summary, details, item)
.onErrorContinue(...)
)
it will run createSummary and then postDetails and if postDetails fails then onErrorContinue will be triggered.
When I trigger a scheduled event via a Command, I do not see the expected Event Handlers trigger. I am trying to isolate a one-time business transaction in a Saga while still allowing Aggregates to be event sourced to be able to replay state changes.
I have configured the following SimpleEventScheduler.
#Bean
public SimpleEventScheduler simpleEventScheduler(EventBus eventBus) {
return SimpleEventScheduler.builder()
.eventBus(eventBus)
.scheduledExecutorService(scheduledExecutorService())
.build();
}
private ScheduledExecutorService scheduledExecutorService() {
return Executors.unconfigurableScheduledExecutorService(Executors.newSingleThreadScheduledExecutor());
}
I have an aggregate modeled that has a #CommandHandler
#CommandHandler
public Letter(ScheduleLetterCommand cmd, EventScheduler scheduler) {
String id = cmd.getLetterId();
log.info("Received schedule command for letter id {}", id);
ScheduleToken scheduleToken = scheduler.schedule(Duration.ofSeconds(5), new BeginSendLetterEvent(id, LetterEventType.BEGIN_SEND));
AggregateLifecycle.apply(new LetterScheduledEvent(id, LetterEventType.SCHEDULED, scheduleToken));
}
and two #EventSourcingHandler
#EventSourcingHandler
public void on(BeginSendLetterEvent event) {
log.info("Letter sending process started {} {}", event.getLetterId(), event.getEventType());
scheduleToken = null;
}
#EventSourcingHandler
public void on(LetterSentEvent event) {
log.info("Letter sent {} {}", event.getLetterId(), event.getEventType());
this.sent = true;
}
I have a saga that does some 'business logic' when BeginSendLetterEvent is triggered and publishes LetterSentEvent.
#Saga
#Slf4j
public class LetterSchedulingSaga {
private EventGateway eventGateway;
public LetterSchedulingSaga() {
//Axon requires empty constructor
}
#StartSaga
#EndSaga
#SagaEventHandler(associationProperty = "letterId")
public void handle(BeginSendLetterEvent event) {
log.info("Sending letter {}...", event.getLetterId());
eventGateway.publish(new LetterSentEvent(event.getLetterId(), LetterEventType.SENT));
}
#Autowired
public void setEventGateway(EventGateway eventGateway) {
this.eventGateway = eventGateway;
}
}
Here is my output:
com.flsh.web.LetterScheduler : Received request to schedule letter
com.flsh.web.LetterScheduler : Finished request to schedule letter
com.flsh.axon.Letter : Received schedule command for letter id b7338082-e0e1-4ba0-b137-c7ff92afe3a1
com.flsh.axon.Letter : LetterScheduledEvent b7338082-e0e1-4ba0-b137-c7ff92afe3a1 SCHEDULED
com.flsh.axon.LetterSchedulingSaga : Sending letter b7338082-e0e1-4ba0-b137-c7ff92afe3a1...
The thing is I am not seeing the above two event handlers being triggered at all. Can someone see what I am doing wrong here? :) Any help would be appreciated...
If this is the wrong way to use Sagas and Event Handlers please let me know. I realize my rudimentary example doesn't facilitate a good domain model.
The short answer to your problem #GoldFish, is that you are expecting to handle events in your Command Model.
The aggregate in Axon terms is a Command Handling Component, as such being part of your Command Model when thinking about CQRS.
As such, it handles command messages and validates whether the given operation (read: command) can be executed. If the outcome of the validation is "yes", that's when you will end up publishing an event in the lifecycle of a given aggregate instances.
The #EventSourcingHandler annotated methods you can introduce into an aggregate are their to "source the aggregate instance based on its own events".
Having said that, you can anticipate that an Aggregate will never handle events directly from any other source then its own.
The EventScheduler is just as much an external source of events as another aggregate's events would be when sourcing. Hence, they will be disregarded for the aggregate.
The EventScheduler will publish an event at a latter stage, so that it might be handled by Event Handling Components, for example Saga instances.
If you want to schedule that something should occur for a specific aggregate or saga instance, you should have a look at the DeadlineManager instead.
Regardless, for what you're trying to achieve, which (I believe) is triggering an operation in your aggregate from a saga, you should use command messages, since the aggregate can only handle command messages.
I am trying to dispatch fault event in GraniteDS service
method call (Flex):
userService.addUser(user, null, function addUserFault(e:TideFaultEvent):void {
Alert.show(e.fault.faultString);
});
server method (Spring):
#Override
public User addUser(User user) throws Exception{
if(findUserByName(user.getUsername()) != null)
throw new Exception("Username Already Exist");
entityManager.persist(user);
return user;
}
But what i get is silence on client side and java.lang.NoSuchMethodException in server console.
How can i use default graniteds exception converter to deliver fault event to client (Flex)?
Solved. I dont know if it's a bug or not, but you cannot set result function to null and specify fault function only - this wont work. My call method should look like:
userService.addUser(user, function addUserResult(e:TideResultEvent){
// do nothing
}, function addUserFault(e:TideFaultEvent):void {
Alert.show(e.fault.faultString);
});
in this case java Exception in remote method will be send back to flex as TideFaultEvent.
I am trying to develop an application in drools,
My rf files is the image shown below.
For the user task ,i have set the actor name as "MyName" .
Now i am reading the rules from this file in my java program.
I want the name of actor.
How i can get the name of actor?
My java code:
TaskServiceSession taskSession = taskService.createSession();
MinaTaskServer server = new MinaTaskServer( taskService);
Thread thread = new Thread( server );
thread.start();
StatefulKnowledgeSession ksession = knowledgeBase.newStatefulKnowledgeSession();
HumanTaskHandler handler1 = new HumanTaskHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Log", handler1);
WSHumanTaskHandler handler = new WSHumanTaskHandler();
ksession.getWorkItemManager().registerWorkItemHandler("Human Task", handler);
KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
// start a new process instance
ksession.startProcess("HumanTaskSample");
Your handler (HumanTaskHandler) will be called whenever a task needs to be created. Whenever it is called, you can get all the necessary information from the work item that is passed as a parameter. So, to get the actor id(s), you can use:
public class HumanTaskHandler implements WorkItemHandler {
public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
String actorId = (String) workItem.getParameter("ActorId");
....
}
...
}
Note that, depending on your requirements, there are some other ways of getting to the task as well. For example, after calling startProcess(..), you could use the returned ProcessInstance to get access to the task id of the task(s) that were created, and then get the full details from the task service. You could also query the task service for any tasks that are assigned to a particular user or group. Or use some of the logging to figure out what happened during the call to startProcess(..).
Kris
I have a newly coded GWT/GAE app that uses RequestFactory and Editors on the client and a custom Objectify DAO Service on the back.
The flush() then persist() paths work fine on success.
Client side JSR 303 works as well as can be expected too.
My question is how to trigger server warnings/errors and handle UI updates?
I am using Chandler's Generic DAO for Objectify 2 at
http://turbomanage.wordpress.com/2010/02/09/generic-dao-for-objectify-2/
my gwt activity is calling persist( myProxy ).fire( new Receiver<> )
my dao code is throwing IllegalArgumentException and other RuntimeExceptions for business logic situations like "Duplicate email address found - want to login instead?"
Receiver<>.onSuccess() works fine to track a successful outcome.
neither Receiver<>.onFailure() nor Receiver<>.onViolation() report the RuntimeExceptions.
( Correction: onFailure() is being called for server-side exceptions)
Is there a better way to do this?
What exceptions should the DAO throw such that onViolation() or onFailure() report errors?
How should the editor(s) handle and recover from the exception?
I've found the most versatile command sequence to be
void start() {
// Either get p
context1.get(..).to( new Receiver<P> { onSuccess(P resp){p = resp;} ... }).fire();
// OR create p
p = context2.create( P.class );
// Then save p
req = context2.persist(p).to( new Receiver<P>{ /* note do not use context1 */
onViolation(...) { /*JSR 303 handler*/ };
onFailure( error ) { /* handle */ error.getMessage() };
onSuccess(X x) { /* whatever persist() returns handler */ }; } );
// drive editor with p
driver.edit( p, req);
}
....
void onSave() {
// editor
ctxt = driver.flush() /* note ctxt == context2 */
if ( driver.hasErrors() ) { /*JSR 303 handler*/};
// RF
ctxt.fire();
}
Based on the conversation excerpt below at http://groups.google.com/group/google-web-toolkit/browse_thread/thread/da863606b3893132/96956661c53e1064?hl=en
Thomas Broyer
onFailure should containg the getMessage() of the
exception you threw on the server
side.
You can tweak it by providing your own
ExceptionHandler to the
RequestFactoryServlet (extend it and
use its constructor taking an
ExceptionHandler).
onViolation will only be called if
your entities do not pass JSR-303 Bean
Validation, which is checked before
calling any service method.
If you want to
"catch" the failure in clidnt code,
you have to add a Receiver for the
persist() service method:
context.persist(p).to(new Receiver…