/**
* Copyright 2014 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.comcast.viper.flume2storm.connection.sender;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.comcast.viper.flume2storm.KryoUtil;
import com.comcast.viper.flume2storm.connection.KryoNetParameters;
import com.comcast.viper.flume2storm.connection.parameters.ConnectionParameters;
import com.comcast.viper.flume2storm.connection.parameters.KryoNetConnectionParameters;
import com.comcast.viper.flume2storm.connection.sender.EventSender;
import com.comcast.viper.flume2storm.event.F2SEvent;
import com.comcast.viper.flume2storm.location.KryoNetServiceProvider;
import com.comcast.viper.flume2storm.utility.circular.CircularList;
import com.comcast.viper.flume2storm.utility.circular.ReadWriteCircularList;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;
/**
* A Flume2Storm event sender implementation using KryoNet framework
*/
public class KryoNetEventSender extends KryoNetServiceProvider implements EventSender<KryoNetConnectionParameters> {
private static final long serialVersionUID = 244675019786272856L;
protected static final Logger LOG = LoggerFactory.getLogger(KryoNetEventSender.class);
protected final EventSenderStats stats;
protected final AtomicReference<KryoNetEventSenderStatus> status;
protected final KryoNetParameters kryoNetParameters;
protected final CircularList<Connection> clients;
protected final Listener clientListener;
protected Server server;
protected KryoNetEventSenderStrategy senderStrategy;
/**
* Contructor
*
* @param connectionParameters
* The {@link ConnectionParameters}
* @param kryoNetParams
* KryoNet specific parameters
*/
public KryoNetEventSender(KryoNetConnectionParameters connectionParameters, KryoNetParameters kryoNetParams) {
super(connectionParameters);
this.kryoNetParameters = kryoNetParams;
stats = new EventSenderStats(connectionParameters.getId());
status = new AtomicReference<KryoNetEventSenderStatus>(KryoNetEventSenderStatus.STOPPED);
clients = new ReadWriteCircularList<Connection>();
clientListener = new Listener.ThreadedListener(new Listener() {
/**
* @see com.esotericsoftware.kryonet.Listener#connected(com.esotericsoftware.kryonet.Connection)
*/
@Override
public void connected(final Connection connection) {
LOG.debug("Connection (id: {}) from {}", connection.getID(), connection.getRemoteAddressTCP());
clients.add(connection);
stats.incrClients();
}
/**
* @see com.esotericsoftware.kryonet.Listener#disconnected(com.esotericsoftware.kryonet.Connection)
*/
@Override
public void disconnected(final Connection connection) {
LOG.debug("Disconnection (id: {})", connection.getID());
clients.remove(connection);
stats.decrClients();
}
});
}
/**
* @see com.comcast.viper.flume2storm.connection.sender.EventSender#getStats()
*/
public EventSenderStats getStats() {
return stats;
}
/**
* @return The list of clients
*/
protected CircularList<Connection> getClients() {
return clients;
}
/**
* @return The status of this {@link EventSender}
*/
public KryoNetEventSenderStatus getStatus() {
return status.get();
}
protected void setStatus(KryoNetEventSenderStatus newStatus) {
assert newStatus != null;
status.set(newStatus);
}
/**
* @see com.comcast.viper.flume2storm.connection.sender.EventSender#start()
*/
public synchronized boolean start() {
if (getStatus().isOn()) {
LOG.warn("Already started");
return true;
}
LOG.trace("Starting KryoNet event distributor...");
setStatus(KryoNetEventSenderStatus.STARTING);
senderStrategy = new KryoNetSimpleRealtimeStrategy(this, kryoNetParameters.getMaxRetries(),
connectionParameters.getWriteBufferSize(), connectionParameters.getObjectBufferSize());
stats.reset();
try {
LOG.debug("Creating Kryo server with connection parameters: {}", connectionParameters);
server = new Server(connectionParameters.getWriteBufferSize(), connectionParameters.getObjectBufferSize());
KryoUtil.register(server.getKryo());
server.addListener(clientListener);
LOG.trace("Binding Kryo server...");
server.bind(connectionParameters.getPort());
LOG.debug("Starting Kryo server...");
server.start();
LOG.info("Kryo Flume server started successfully with {}", connectionParameters);
setStatus(KryoNetEventSenderStatus.STARTED);
return true;
} catch (final Exception e) {
LOG.error("Failed to start server with " + connectionParameters, e);
stop();
return false;
}
}
/**
* @see com.comcast.viper.flume2storm.connection.sender.EventSender#stop()
*/
public synchronized boolean stop() {
if (!getStatus().isOn()) {
LOG.warn("Already stopped");
return true;
}
boolean result = true;
try {
LOG.debug("Stopping KryoNet server...");
server.stop();
} catch (final Exception e) {
LOG.error("Failed to stop KryoNet server", e);
result = false;
}
setStatus(KryoNetEventSenderStatus.STOPPED);
return result;
}
/**
* @see com.comcast.viper.flume2storm.connection.sender.EventSender#send(java.util.List)
*/
@Override
public int send(List<F2SEvent> events) {
if (!getStatus().isOn()) {
LOG.error("Event sender cannot send event if it is not started");
return 0;
}
if (clients.isEmpty())
return 0;
return senderStrategy.send(events);
}
}