Axon Register Tracking Processor with distributed query model - spring-boot

I had implement CQRS+ES application using axon and spring-boot. I use separate query model and command model application. I use rabbitmq to publish event from command mode. It works correct. But tracking Processor implementation is not work in my application.
This is my query model
#SpringBootApplication
public class SeatQueryPart1Application {
public static void main(String[] args) {
SpringApplication.run(SeatQueryPart1Application.class, args);
}
#Bean
public SpringAMQPMessageSource statisticsQueue(Serializer serializer) {
return new SpringAMQPMessageSource(new DefaultAMQPMessageConverter(serializer)) {
#RabbitListener(exclusive = false, bindings = #QueueBinding(value = #Queue, exchange = #Exchange(value = "ExchangeTypesTests.FanoutExchange", type = ExchangeTypes.FANOUT), key = "orderRoutingKey"))
#Override
public void onMessage(Message arg0, Channel arg1) throws Exception {
super.onMessage(arg0, arg1);
}
};
}
#Autowired
public void conf(EventHandlingConfiguration configuration) {
configuration.registerTrackingProcessor("statistics");
}
}
this is a event handler class
#ProcessingGroup("statistics")
#Component
public class EventLoggingHandler {
private SeatReservationRepository seatReservationRepo;
public EventLoggingHandler(final SeatReservationRepository e) {
this.seatReservationRepo = e;
}
#EventHandler
protected void on(SeatResurvationCreateEvent event) {
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
Seat seat=new Seat(event.getId(), event.getSeatId(), event.getDate(),timestamp ,true);
seatReservationRepo.save(seat);
}
}
this is yml configuration
axon:
eventhandling:
processors:
statistics.source: statisticsQueue
How can i do it correct. (Can anyone suggest tutorial or code sample)

The SpringAMQPMessageSource is a SubscribableMessageSource. This means you cannot use a tracking event processor to process messages. It is only compatible with a Subscribable Event Processor.
Removing configuration.registerTrackingProcessor("statistics"); and leaving it to the default (subscribing) should do the trick.

Related

Get session events from Hazelcast session repository?

I'm not getting session closed or expired events when using embedded Hazelcast session repository in Spring boot application. I do get session creation events. I have a very short timeout for the sessions (30s). I have verified that the session gets expired after 30s by getting "unauthorized" reply from the server. How do you receive session expiration/destruction events?
This is my session configuration:
#Configuration
#EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = 30)
public class SessionConfiguration{
#Bean
#SpringSessionHazelcastInstance
public HazelcastInstance hazelcastInstance() {
Config config = new Config();
config.setClusterName("spring-session-cluster");
// Add this attribute to be able to query sessions by their PRINCIPAL_NAME_ATTRIBUTE's
AttributeConfig attributeConfig = new AttributeConfig()
.setName(Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractorClassName(Hazelcast4PrincipalNameExtractor.class.getName());
// Configure the sessions map
config.getMapConfig(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addAttributeConfig(attributeConfig).addIndexConfig(
new IndexConfig(IndexType.HASH, Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE));
// Use use custom serializer to de/serialize sessions faster. This is optional.
SerializerConfig serializerConfig = new SerializerConfig();
serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class);
config.getSerializationConfig().addSerializerConfig(serializerConfig);
return Hazelcast.newHazelcastInstance(config);
}
#Bean
public SessionRepositoryCustomizer<Hazelcast4IndexedSessionRepository> customize() {
return (sessionRepository) -> {
sessionRepository.setFlushMode(FlushMode.IMMEDIATE);
sessionRepository.setSaveMode(SaveMode.ALWAYS);
sessionRepository.setSessionMapName(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME);
sessionRepository.setDefaultMaxInactiveInterval(30); //this is extra; tried with and without
};
}
}
And this is my listener:
#Component
public class SessionListener {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SessionListener.class);
#EventListener
public void sessionCreated(SessionCreatedEvent event) {
log.info("SESSION:CREATE:ID="+event.getSessionId()); //only this gets called but none of the others
}
#EventListener
public void sessionDeleted(SessionDeletedEvent event) {
log.info("SESSION:DELETE:ID="+event.getSessionId());
}
#EventListener
public void sessionDestroyed(SessionDestroyedEvent event) {
log.info("SESSION:DESTROY:ID="+event.getId());
}
#EventListener
public void sessionExpired(SessionExpiredEvent event) {
log.info("SESSION:EXPIRE:ID="+event.getSessionId());
}
}
Partial answer (I don't know exactly why it works):
If you add a session map listener to HazelcastInstance (in the creating bean) you suddenly start receiving SessionExpiredEvents.
So replace lines:
config.getMapConfig(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addAttributeConfig(attributeConfig).addIndexConfig(
new IndexConfig(IndexType.HASH, Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE));
with (set maximum idle seconds to the session map configuration):
config.getMapConfig(Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addAttributeConfig(attributeConfig).addIndexConfig(
new IndexConfig(IndexType.HASH, Hazelcast4IndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE))
.setMaxIdleSeconds(tout);
and
return Hazelcast.newHazelcastInstance(config);
with (add session entry listener to the session map)
HazelcastInstance instance = Hazelcast.newHazelcastInstance(config);
IMap<Object, Object> map = instance.getMap( Hazelcast4IndexedSessionRepository.DEFAULT_SESSION_MAP_NAME );
map.addEntryListener( new HazelcastSessionEntryListener(), true );
return instance;
where HazelcastSessionEntryListener can be defined like this:
#Component
public class HazelcastSessionEntryListener implements EntryListener<Object, Object>
{
public HazelcastSessionEntryListener(){}
#Override
public void entryAdded(EntryEvent<Object, Object> event){}
#Override
public void entryUpdated(EntryEvent<Object, Object> event){}
#Override
public void entryRemoved(EntryEvent<Object, Object> event){}
#Override
public void entryEvicted(EntryEvent<Object, Object> event){}
#Override
public void entryExpired(EntryEvent<Object, Object> event){}
#Override
public void mapCleared(MapEvent event){}
#Override
public void mapEvicted(MapEvent event){}
}
Funny thing is that HazelcastSessionEntryListener is just an empty implementation in my case (it doesn't do anything). Seems like buggy behaviour (but I'm not a Spring expert).

How do I use a different Spring datasource depending on the name of the unit test?

)
I have a rather unusual requirement for a series of unit tests I'm fixing. Basically, a different datasource/transaction manager needs to be used depending on the method name of the unit test.
For example, if the test name ends with UseDB2, then we use the DB2 data source, if it's UseH2 then we use the H2 datasource.
I thought the way to go about this was to use the AbstractRoutingDatasource provided by the Spring framework.
public class TestRoutingDatasSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
Using a context holder to select the required datasource:
public class DatabaseContextHolder {
private static final ThreadLocal<DType> contextHolder = new ThreadLocal<DType>();
public static void setDatabaseType(DType databaseType) {
contextHolder.set(databaseType);
}
public static DType getDatabaseType() {
return (DType) contextHolder.get();
}
public static void clearDatabaseType() {
contextHolder.remove();
}
}
I was then going to use the name of the test to set the context; something like this:
public class MyDBUnitTestCase extends
AbstractTransactionalDataSourceSpringContextTests {
protected DataSource dataSource;
protected String schemaName;
public void setDataSource(DataSource aDataSource) {
this.dataSource = aDataSource;
}
public void setSchemaName(String aSchemaName) {
this.schemaName = aSchemaName;
}
protected void onSetUp() throws Exception {
super.onSetUp();
if (getName().endsWith("UsingDB2")) {
DatabaseContextHolder.setDatabaseType(DType.DB2);
}
else {
DatabaseContextHolder.setDatabaseType(DType.H2);
}
}
But of course, this won't work because by the time I've come to check the name of the test, Spring has already configured all the beans (doh!).
Is there some other mechanism I can use to get this to work?
Thanks very much for your time.

Spring boot graceful shutdown mid-transaction

I'm working on a spring-boot service that performs sensitive payment processing, and would like to ensure that any shutdown to the app will be done without interrupting these transactions. Curious on how to best approach this in spring-boot.
I read about adding shutdown hooks to spring-boot, and I was thinking maybe to use a CountDownLatch on the class to check if the thread has completed processing - something like this:
#Service
public class PaymentService {
private CountDownLatch countDownLatch;
private void resetLatch() {
this.countDownLatch = new CountDownLatch(1);
}
public void processPayment() {
this.resetLatch();
// do multi-step processing
this.CountDownLatch.countDown();
}
public void shutdown() {
// blocks until latch is available
this.countDownLatch.await();
}
}
// ---
#SpringBootApplication
public class Application {
public static void main(String[] args) {
// init app and get context
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
// retrieve bean needing special shutdown care
PaymentService paymentService = context.getBean(PaymentService.class);
Runtime.getRuntime().addShutdownHook(new Thread(paymentService::shutdown));
}
}
Constructive feedback is greatly appreciated - thanks.
I ended up using #PreDestroy annotation on the shutdown method:
#Service
public class PaymentService {
private CountDownLatch countDownLatch;
private synchronized void beginTransaction() {
this.countDownLatch = new CountDownLatch(1);
}
private synchronized void endTransaction() {
this.countDownLatch.countDown();
}
public void processPayment() {
try {
this.beginTransaction();
// - - - -
// do multi-step processing
// - - - -
} finally {
this.endTransaction();
}
}
#PreDestroy
public void shutdown() {
// blocks until latch is available
this.countDownLatch.await();
}
}

Spring Websocket - How can I detect client disconnect

I am new to spring
I have this class :
public class Server extends TextWebSocketHandler implements WebSocketHandler {
WebSocketSession clientsession;
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
clientsession = session;
}
I need to detect a client disconnect on clientsession.
implement ApplicationListener but its not clear how I can register the listener?
do I need to do it in my web.xml ?
The WebSocketHandler afterConnectionClosed function is called after a websocket client disconnects. You simply need to override this in the manner that you override handleTextMessage.
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus){
// your code here
}
There may be substantial delay between the client disconnect and your server event detection. See details about real-time disconnection detection.
You will need to override configureClientOutboundChannel and configureClientInboundChannel of AbstractWebSocketMessageBrokerConfigurer, providing your interceptor
Another way is using ApplicationEvents.
Both methods are described here:
http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/
public class StompConnectEvent implements ApplicationListener<SessionConnectEvent> {
private final Log logger = LogFactory.getLog(StompConnectEvent.class);
public void onApplicationEvent(SessionConnectEvent event) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
String company = sha.getNativeHeader("company").get(0);
logger.debug("Connect event [sessionId: " + sha.getSessionId() +"; company: "+ company + " ]");
}
}
I hope that help. Let me know if I need to explain more.
You can use listeners to detect when session is connected or closed.
More information about listeners you can find by this link.
Example how to detect connected session:
#Component
public class SessionConnectedEventListener implements ApplicationListener<SessionConnectedEvent> {
private IWebSocketSessionService webSocketSessionService;
public SessionConnectedEventListener(IWebSocketSessionService webSocketSessionService) {
this.webSocketSessionService = webSocketSessionService;
}
#Override
public void onApplicationEvent(SessionConnectedEvent event) {
webSocketSessionService.saveSession(event);
}
}
Example how to detect when session is disconneted:
#Component
public class SessionDisconnectEventListener implements ApplicationListener<SessionDisconnectEvent> {
private IWebSocketSessionService webSocketSessionService;
public SessionDisconnectEventListener(IWebSocketSessionService webSocketSessionService) {
this.webSocketSessionService = webSocketSessionService;
}
#Override
public void onApplicationEvent(SessionDisconnectEvent event) {
webSocketSessionService.removeSession(event);
}
}
What you probably want to achieve is maintaining multiple sessions. Something like this:
public class Server extends TextWebSocketHandler implements WebSocketHandler {
private List<WebSocketSession> sessions = new ArrayList<>();
#Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
sessions.remove(session);
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// clientsession = session;
// Send individual or broadcast messages here ...
session.sendMessage(new TextMessage(textMessage + "!"));
}
}

JMeter Plugin - How to Listen to TestState

I am working on developing a JMeter plugin. I'm trying to create an AbstractVisualizer that is capable of monitoring the current test state. However, implementing the TestStateListener doesn't seem to be working.
I'm testing this by creating a basic listener that has a login to output arbitrary info to JMeter's logging console. When a sample is sent through the Add function, a line is sent to the console. But nothing is ever triggered on the various TestState functions. Is there something more structural I'm missing?
public class TestListener extends AbstractVisualizer
implements TestStateListener
{
private static final Logger log = LoggingManager.getLoggerForClass();
#Override
public void add(SampleResult arg0) {
log.info("add");
}
#Override
public void clearData() {
// TODO Auto-generated method stub
}
#Override
public String getStaticLabel()
{
return "Test Listener";
}
#Override
public String getLabelResource() {
return null;
}
#Override
public void testEnded() {
log.info("Test Ended");
}
#Override
public void testEnded(String arg0) {
log.info("Test Ended");
}
#Override
public void testStarted() {
log.info("Test started");
}
#Override
public void testStarted(String arg0) {
log.info("Test started");
}
}
I'm not sure how to do it in 1 class. I have 2 classes:
The UI:
public class MonitorGui extends AbstractListenerGui
{
// ...
#Override
public TestElement createTestElement()
{
TestElement element = new Monitor();// <-- this is the backend
modifyTestElement(element);
return element;
}
// ...
}
And then the backend goes like this:
public class Monitor extends AbstractListenerElement
implements SampleListener,
Clearable, Serializable,
TestStateListener, Remoteable,
NoThreadClone
{
private static final String TEST_IS_LOCAL = "*local*";
// ...
#Override
public void testStarted()
{
testStarted(TEST_IS_LOCAL);
}
#Override
public void testEnded()
{
testEnded(TEST_IS_LOCAL);
}
#Override
public void testStarted(String host)
{
// ...
}
// ...
}
You may not need to implement SampleListener like I do, but probably other things are quite similar.
I based that implementation on a built-in pair of ResultSaverGui and ResultCollector which are the components that are saving results into the file(s) for Simple Data Writer, Summary Report and so on.

Resources