/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.cluster;
import org.geoserver.cluster.events.ToggleType;
import org.springframework.jms.listener.SessionAwareMessageListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
/**
* A simple JMS events listener for our tests.
*/
public final class JmsEventsListener extends JMSApplicationListener implements SessionAwareMessageListener<Message> {
public enum Status {
NO_STATUS, SELECT_CONTINUE, REJECT_CONTINUE, SELECT_STOP, REJECT_STOP
}
private static final List<Message> messages = new ArrayList<>();
public JmsEventsListener() {
super(ToggleType.SLAVE);
}
@Override
public void onMessage(Message message, Session session) throws JMSException {
synchronized (messages) {
// we just need to store the received message
messages.add(message);
}
}
public static void clear() {
synchronized (messages) {
// clear the processing pending messages
messages.clear();
}
}
/**
* Blocking helper method that allows us to wait for certain messages in a certain time.
* The stop method will be used to check if we have all the messages we need. Only
* messages that match one of the provided handlers keys will be selected.
*/
public static List<Message> getMessagesByHandlerKey(int timeoutMs, Function<List<Message>, Boolean> stop, String... keys) {
List<String> keysList = Arrays.asList(keys);
return JmsEventsListener.getMessages(timeoutMs, stop,
(message) -> {
try {
String handlerKey = message.getStringProperty(JMSEventHandlerSPI.getKeyName());
if (keysList.contains(handlerKey)) {
// we want this message
return Status.SELECT_CONTINUE;
}
} catch (Exception exception) {
// we got an exception let's just ignore this message
}
// not the message we want
return Status.REJECT_CONTINUE;
});
}
/**
* Blocking helper method that allows us to wait for certain messages in a certain time.
* The stop method will be used to check if we have all the messages we need. The selector
* method is used to select only certain messages.
*/
public static List<Message> getMessages(int timeoutMs, Function<List<Message>, Boolean> stop, Function<Message, Status> selector) {
List<Message> selected = new ArrayList<>();
Status status = Status.NO_STATUS;
int max = (int) Math.ceil(timeoutMs / 10.0);
int i = 0;
while (i < max && status != Status.SELECT_STOP && status != Status.REJECT_STOP && !stop.apply(selected)) {
try {
// let's wait ten milliseconds
Thread.sleep(10);
} catch (InterruptedException exception) {
// restore the interrupted status and return the current messages we have
Thread.currentThread().interrupt();
return selected;
}
i++;
synchronized (messages) {
for (Message message : messages) {
status = selector.apply(message);
if (status == Status.SELECT_CONTINUE || status == Status.SELECT_STOP) {
// we want this message
selected.add(message);
}
if (status == Status.SELECT_STOP || status == Status.REJECT_STOP) {
// we are done
break;
}
}
// clear all processed messages
messages.clear();
}
}
return selected;
}
/**
* Searches the events that match a certain handler and apply the handler to those elements.
*/
public static <T> List<T> getMessagesForHandler(List<Message> messages, String handlerName, JMSEventHandler<String, T> handler) {
List<T> found = new ArrayList<>();
for (Message message : messages) {
try {
String handlerKey = message.getStringProperty(JMSEventHandlerSPI.getKeyName());
if (handlerKey.equals(handlerName) && message instanceof ObjectMessage) {
// we found a message that match's the desired handler
String object = ((ObjectMessage) message).getObject().toString();
found.add(handler.deserialize(object));
}
} catch (Exception exception) {
// we got an exception let's just ignore this message
}
}
return found;
}
}