
org.jolokia.service.notif.sse.SseNotificationBackend Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jolokia-service-notif-sse Show documentation
Show all versions of jolokia-service-notif-sse Show documentation
Jolokia :: Service :: Notification :: SSE
package org.jolokia.service.notif.sse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.management.AttributeNotFoundException;
import javax.management.Notification;
import org.jolokia.json.JSONStructure;
import org.jolokia.server.core.http.BackChannel;
import org.jolokia.server.core.service.api.AbstractJolokiaService;
import org.jolokia.server.core.service.api.JolokiaContext;
import org.jolokia.server.core.service.notification.*;
import org.jolokia.server.core.service.serializer.SerializeOptions;
import org.jolokia.server.core.service.serializer.Serializer;
import org.jolokia.json.JSONObject;
/**
* Dummy implementation
*
* @author roland
* @since 20.03.13
*/
public class SseNotificationBackend extends AbstractJolokiaService implements NotificationBackend {
private static final byte[] CRLF = new byte[]{'\r', '\n'};
private static final byte[] ID_FIELD = "id: ".getBytes(StandardCharsets.UTF_8);
private static final byte[] DATA_FIELD = "data: ".getBytes(StandardCharsets.UTF_8);
// Map with heart beat threads
private final HashMap heartBeatMap = new HashMap<>();
/**
* Create a server side event notification backend which will return notifications
* via the backchannel.
*
* @param order of this notification backend
*/
public SseNotificationBackend(int order) {
super(NotificationBackend.class,order);
}
/** {@inheritDoc} */
public void init(JolokiaContext pContext) {
if (getJolokiaContext() == null) {
super.init(pContext);
}
}
@Override
public void destroy() throws Exception {
if (getJolokiaContext() != null) {
super.destroy();
}
}
/** {@inheritDoc} */
public String getNotifType() {
return "sse";
}
/** {@inheritDoc} */
public BackendCallback subscribe(final NotificationSubscription pSubscription) {
final Client client = pSubscription.getClient();
return new BackendCallback() {
/** {@inheritDoc} */
public void handleNotification(Notification notification, Object handback) {
BackChannel backChannel = client.getBackChannel(getNotifType());
if (backChannel != null && !backChannel.isClosed()) {
JolokiaContext ctx = getJolokiaContext();
Serializer serializer = ctx.getMandatoryService(Serializer.class);
NotificationResult result =
new NotificationResult(pSubscription.getHandle(), Collections.singletonList(notification),
handback, 0);
try {
long id = notification.getSequenceNumber();
JSONStructure json =
(JSONStructure) serializer.serialize(result, null /* no path */, SerializeOptions.DEFAULT);
StringWriter sw = new StringWriter();
json.writeJSONString(sw);
sendMessage(backChannel,id,sw.toString());
} catch (IOException e) {
// TODO: Collect in a buffer, ordered by sequence number
} catch (AttributeNotFoundException e) {
// No path, no exception, so cant happen (TM) in 'serialize()'
}
}
// TODO: Collect exception in client specific buffer and send it when a reconnect happened
}
};
}
public void channelInit(Client client, BackChannel channel) {
// Start heartbeat
SseHeartBeat heartBeat = new SseHeartBeat(channel);
heartBeat.start();
heartBeatMap.put(client.getId(),heartBeat);
}
/** {@inheritDoc} */
public void unsubscribe(String pClientId, String pHandle) {
// TODO: Clean up any store notifications
}
/** {@inheritDoc}
* @param pClient*/
public void unregister(Client pClient) {
// Stop heartbeat
SseHeartBeat heartBeat = heartBeatMap.remove(pClient.getId());
if (heartBeat != null) {
heartBeat.stop();
}
// Close channel.
BackChannel backChannel = pClient.getBackChannel(getNotifType());
if (backChannel != null) {
backChannel.close();
}
}
/** {@inheritDoc} */
public Map getConfig() {
JSONObject ret = new JSONObject();
ret.put(BackChannel.CONTENT_TYPE, "text/event-stream");
ret.put(BackChannel.ENCODING, "UTF-8");
return ret;
}
// ===================================================================================
private void sendMessage(BackChannel pBackChannel, long pId, String pData) throws IOException {
synchronized (pBackChannel) {
OutputStream os = pBackChannel.getOutputStream();
id(os,pId);
data(os,pData);
os.write(CRLF);
os.flush();
}
}
private void id(OutputStream pOs, long pId) throws IOException {
printLine(pOs, ID_FIELD, Long.toString(pId).getBytes(StandardCharsets.UTF_8));
}
private void data(OutputStream pOs, String pData) throws IOException {
synchronized (this) {
BufferedReader reader = new BufferedReader(new StringReader(pData));
String line;
while ((line = reader.readLine()) != null) {
printLine(pOs, DATA_FIELD, line.getBytes(StandardCharsets.UTF_8));
}
}
}
private void printLine(OutputStream pOs, byte[] pField, byte[] pValue) throws IOException {
pOs.write(pField);
pOs.write(pValue);
pOs.write(CRLF);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy