/******************************************************************************* * Copyright (c) 2013-2015 Sierra Wireless and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.html. * * Contributors: * Sierra Wireless - initial API and implementation *******************************************************************************/ package org.eclipse.leshan.server.demo.servlet; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.http.HttpServletRequest; import org.eclipse.californium.core.network.Endpoint; import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.response.ObserveResponse; import org.eclipse.leshan.server.californium.impl.LeshanServer; import org.eclipse.leshan.server.demo.servlet.json.LwM2mNodeSerializer; import org.eclipse.leshan.server.demo.servlet.json.RegistrationSerializer; import org.eclipse.leshan.server.demo.servlet.log.CoapMessage; import org.eclipse.leshan.server.demo.servlet.log.CoapMessageListener; import org.eclipse.leshan.server.demo.servlet.log.CoapMessageTracer; import org.eclipse.leshan.server.demo.utils.EventSource; import org.eclipse.leshan.server.demo.utils.EventSourceServlet; import org.eclipse.leshan.server.observation.ObservationListener; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.RegistrationListener; import org.eclipse.leshan.server.registration.RegistrationUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.GsonBuilder; public class EventServlet extends EventSourceServlet { private static final String EVENT_DEREGISTRATION = "DEREGISTRATION"; private static final String EVENT_UPDATED = "UPDATED"; private static final String EVENT_REGISTRATION = "REGISTRATION"; private static final String EVENT_NOTIFICATION = "NOTIFICATION"; private static final String EVENT_COAP_LOG = "COAPLOG"; private static final String QUERY_PARAM_ENDPOINT = "ep"; private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(EventServlet.class); private final Gson gson; private final CoapMessageTracer coapMessageTracer; private Set<LeshanEventSource> eventSources = Collections .newSetFromMap(new ConcurrentHashMap<LeshanEventSource, Boolean>()); private final RegistrationListener registrationListener = new RegistrationListener() { @Override public void registered(Registration registration) { String jReg = EventServlet.this.gson.toJson(registration); sendEvent(EVENT_REGISTRATION, jReg, registration.getEndpoint()); } @Override public void updated(RegistrationUpdate update, Registration updatedRegistration, Registration previousRegistration) { String jReg = EventServlet.this.gson.toJson(updatedRegistration); sendEvent(EVENT_UPDATED, jReg, updatedRegistration.getEndpoint()); } @Override public void unregistered(Registration registration, Collection<Observation> observations, boolean expired) { String jReg = EventServlet.this.gson.toJson(registration); sendEvent(EVENT_DEREGISTRATION, jReg, registration.getEndpoint()); } }; private final ObservationListener observationListener = new ObservationListener() { @Override public void cancelled(Observation observation) { } @Override public void onResponse(Observation observation, Registration registration, ObserveResponse response) { if (LOG.isDebugEnabled()) { LOG.debug("Received notification from [{}] containing value [{}]", observation.getPath(), response.getContent().toString()); } if (registration != null) { String data = new StringBuilder("{\"ep\":\"").append(registration.getEndpoint()).append("\",\"res\":\"") .append(observation.getPath().toString()).append("\",\"val\":") .append(gson.toJson(response.getContent())).append("}").toString(); sendEvent(EVENT_NOTIFICATION, data, registration.getEndpoint()); } } @Override public void onError(Observation observation, Registration registration, Exception error) { if (LOG.isWarnEnabled()) { LOG.warn(String.format("Unable to handle notification of [%s:%s]", observation.getRegistrationId(), observation.getPath()), error); } } @Override public void newObservation(Observation observation, Registration registration) { } }; public EventServlet(LeshanServer server, int securePort) { server.getRegistrationService().addListener(this.registrationListener); server.getObservationService().addListener(this.observationListener); // add an interceptor to each endpoint to trace all CoAP messages coapMessageTracer = new CoapMessageTracer(server.getRegistrationService()); for (Endpoint endpoint : server.getCoapServer().getEndpoints()) { endpoint.addInterceptor(coapMessageTracer); } GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeHierarchyAdapter(Registration.class, new RegistrationSerializer(securePort)); gsonBuilder.registerTypeHierarchyAdapter(LwM2mNode.class, new LwM2mNodeSerializer()); gsonBuilder.setDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); this.gson = gsonBuilder.create(); } private synchronized void sendEvent(String event, String data, String endpoint) { if (LOG.isDebugEnabled()) { LOG.debug("Dispatching {} event from endpoint {}", event, endpoint); } for (LeshanEventSource eventSource : eventSources) { if (eventSource.getEndpoint() == null || eventSource.getEndpoint().equals(endpoint)) { eventSource.sentEvent(event, data); } } } class ClientCoapListener implements CoapMessageListener { private final String endpoint; ClientCoapListener(String endpoint) { this.endpoint = endpoint; } @Override public void trace(CoapMessage message) { String coapLog = EventServlet.this.gson.toJson(message); sendEvent(EVENT_COAP_LOG, coapLog, endpoint); } } private void cleanCoapListener(String endpoint) { // remove the listener if there is no more eventSources for this endpoint for (LeshanEventSource eventSource : eventSources) { if (eventSource.getEndpoint() == null || eventSource.getEndpoint().equals(endpoint)) { return; } } coapMessageTracer.removeListener(endpoint); } @Override protected EventSource newEventSource(HttpServletRequest req) { String endpoint = req.getParameter(QUERY_PARAM_ENDPOINT); return new LeshanEventSource(endpoint); } private class LeshanEventSource implements EventSource { private String endpoint; private Emitter emitter; public LeshanEventSource(String endpoint) { this.endpoint = endpoint; } @Override public void onOpen(Emitter emitter) throws IOException { this.emitter = emitter; eventSources.add(this); if (endpoint != null) { coapMessageTracer.addListener(endpoint, new ClientCoapListener(endpoint)); } } @Override public void onClose() { cleanCoapListener(endpoint); eventSources.remove(this); } public void sentEvent(String event, String data) { try { emitter.event(event, data); } catch (IOException e) { onClose(); } } public String getEndpoint() { return endpoint; } } }