/* * Copyright (c) 2012-2015 The original author or authors * ------------------------------------------------------ * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package org.eclipse.moquette.spi.impl; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.eclipse.moquette.proto.messages.AbstractMessage; import org.eclipse.moquette.spi.IMessagesStore; import org.eclipse.moquette.spi.IMessaging; import org.eclipse.moquette.spi.ISessionsStore; import org.eclipse.moquette.spi.impl.events.LostConnectionEvent; import org.eclipse.moquette.spi.impl.events.MessagingEvent; import org.eclipse.moquette.spi.impl.events.ProtocolEvent; import org.eclipse.moquette.spi.impl.events.StopEvent; import org.eclipse.moquette.spi.impl.subscriptions.SubscriptionsStore; import org.eclipse.moquette.spi.persistence.MapDBPersistentStore; import org.red5.server.mqtt.IAuthenticator; import org.red5.server.mqtt.ServerChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.lmax.disruptor.EventHandler; import com.lmax.disruptor.RingBuffer; import com.lmax.disruptor.dsl.Disruptor; /** * Singleton class that orchestrate the execution of the protocol. * * Uses the LMAX Disruptor to serialize the incoming, requests, because it work in a evented fashion; the requests come from connectors and are dispatched to the ProtocolProcessor. * * @author andrea */ public class SimpleMessaging implements IMessaging, EventHandler<ValueEvent> { private static final Logger LOG = LoggerFactory.getLogger(SimpleMessaging.class); private static SimpleMessaging INSTANCE; private final ProtocolProcessor processor = new ProtocolProcessor(); private final AnnotationSupport annotationSupport = new AnnotationSupport(); private SubscriptionsStore subscriptions; private RingBuffer<ValueEvent> ringBuffer; private IMessagesStore storageService; private ISessionsStore sessionsStore; private ExecutorService executor; private Disruptor<ValueEvent> disruptor; private IAuthenticator authenticator; private MapDBPersistentStore mapStorage; CountDownLatch stopLatch; private SimpleMessaging() { } public static SimpleMessaging getInstance() { if (INSTANCE == null) { INSTANCE = new SimpleMessaging(); } return INSTANCE; } public void init() { subscriptions = new SubscriptionsStore(); executor = Executors.newFixedThreadPool(1); disruptor = new Disruptor<>(ValueEvent.EVENT_FACTORY, 1024 * 32, executor); /*Disruptor<ValueEvent> m_disruptor = new Disruptor<ValueEvent>(ValueEvent.EVENT_FACTORY, 1024 * 32, m_executor, ProducerType.MULTI, new BusySpinWaitStrategy());*/ disruptor.handleEventsWith(this); disruptor.start(); // Get the ring buffer from the Disruptor to be used for publishing ringBuffer = disruptor.getRingBuffer(); // annotationSupport.processAnnotations(processor); // link to storage storageService = mapStorage; sessionsStore = mapStorage; // init storage storageService.initStore(); // init subscriptions subscriptions.init(sessionsStore); // init processor processor.init(subscriptions, storageService, sessionsStore, authenticator); // disruptorPublish(new InitEvent(configProps)); } private void disruptorPublish(MessagingEvent msgEvent) { LOG.debug("disruptorPublish publishing event {}", msgEvent); long sequence = ringBuffer.next(); ValueEvent event = ringBuffer.get(sequence); event.setEvent(msgEvent); ringBuffer.publish(sequence); } @Override public void lostConnection(ServerChannel session, String clientID) { disruptorPublish(new LostConnectionEvent(session, clientID)); } @Override public void handleProtocolMessage(ServerChannel session, AbstractMessage msg) { disruptorPublish(new ProtocolEvent(session, msg)); } @Override public void stop() { stopLatch = new CountDownLatch(1); disruptorPublish(new StopEvent()); try { //wait the callback notification from the protocol processor thread LOG.debug("waiting 10 sec to m_stopLatch"); boolean elapsed = !stopLatch.await(10, TimeUnit.SECONDS); LOG.debug("after m_stopLatch"); executor.shutdown(); disruptor.shutdown(); if (elapsed) { LOG.error("Can't stop the server in 10 seconds"); } } catch (InterruptedException ex) { LOG.error(null, ex); } } @Override public void onEvent(ValueEvent t, long l, boolean bln) throws Exception { MessagingEvent evt = t.getEvent(); LOG.info("onEvent processing messaging event from input ringbuffer {}", evt); if (evt instanceof StopEvent) { processStop(); return; } if (evt instanceof LostConnectionEvent) { LostConnectionEvent lostEvt = (LostConnectionEvent) evt; processor.processConnectionLost(lostEvt); return; } if (evt instanceof ProtocolEvent) { ServerChannel session = ((ProtocolEvent) evt).getSession(); AbstractMessage message = ((ProtocolEvent) evt).getMessage(); try { annotationSupport.dispatch(session, message); } catch (Throwable th) { LOG.error("Grave error processing the message {} for {}", message, session, th); } } } private void processStop() { LOG.debug("processStop invoked"); storageService.close(); LOG.debug("subscription tree {}", subscriptions.dumpTree()); subscriptions = null; stopLatch.countDown(); } public void setAuthenticator(IAuthenticator authenticator) { this.authenticator = authenticator; } public void setMapStorage(MapDBPersistentStore mapStorage) { this.mapStorage = mapStorage; } }