/*
* KitchenSync-core Java Library Copyright (C) 2014 Burton Alexander
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mrstampy.kitchensync.stream;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.mrstampy.kitchensync.netty.channel.KiSyChannel;
import com.github.mrstampy.kitchensync.stream.footer.EndOfMessageFooter;
import com.github.mrstampy.kitchensync.stream.footer.Footer;
import com.github.mrstampy.kitchensync.stream.inbound.EndOfMessageInboundMessageHandler;
/**
* The Register for end of message messages.
*
* @see EndOfMessageListener
* @see EndOfMessageInboundMessageHandler
* @see Footer
*/
public class EndOfMessageRegister {
private static final Logger log = LoggerFactory.getLogger(EndOfMessageRegister.class);
/**
* The Constant INSTANCE, convenience singleton using
* {@link EndOfMessageFooter}.
*/
public static final EndOfMessageRegister INSTANCE = new EndOfMessageRegister(new EndOfMessageFooter());
private List<EndOfMessageListener> eomListeners = new ArrayList<EndOfMessageListener>();
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private ReadLock readLock = lock.readLock();
private WriteLock writeLock = lock.writeLock();
private Footer footer;
private Lock footerLock = new ReentrantLock();
/**
* Instantiate with the {@link Footer} instance used to indicate
* {@link #isEndOfMessage(byte[])}. No nulls allowed.
*
* @param footer
* to indicate {@link #isEndOfMessage(byte[])}.
* @see Footer#isFooter(byte[])
*/
public EndOfMessageRegister(Footer footer) {
setFooter(footer);
}
/**
* Adds the eom listeners.
*
* @param listeners
* the listeners
*/
public void addEOMListeners(EndOfMessageListener... listeners) {
if (listeners == null || listeners.length == 0) return;
writeLock.lock();
try {
for (EndOfMessageListener l : listeners) {
eomListeners.add(l);
}
} finally {
writeLock.unlock();
}
}
/**
* Removes the eom listeners.
*
* @param listeners
* the listeners
*/
public void removeEOMListeners(EndOfMessageListener... listeners) {
if (listeners == null || listeners.length == 0) return;
writeLock.lock();
try {
for (EndOfMessageListener l : listeners) {
eomListeners.remove(l);
}
} finally {
writeLock.unlock();
}
}
/**
* Notify eom listeners. Assumes that {@link #isEndOfMessage(byte[])} has been
* used appropriately.
*
* @param channel
* the channel
* @param sender
* the sender
* @param eom
* the end of message message
*/
public void notifyEOMListeners(KiSyChannel channel, InetSocketAddress sender, byte[] eom) {
readLock.lock();
try {
boolean notified = false;
for (EndOfMessageListener l : eomListeners) {
if (!l.isForChannelAndSender(channel, sender)) continue;
l.endOfMessage(eom);
notified = true;
}
if (!notified) log.warn("No listener for end of message from {} on channel {}", sender, channel.getPort());
} finally {
readLock.unlock();
}
}
/**
* Returns true if the specified message is an end of message message.
*
* @param message
* the message
* @return true, if checks if is end of message
* @see EndOfMessageInboundMessageHandler
*/
public boolean isEndOfMessage(byte[] message) {
footerLock.lock();
try {
return getFooter().isFooter(message);
} finally {
footerLock.unlock();
}
}
/**
* Gets the footer used to determine if {@link #isEndOfMessage(byte[])}. The
* default is a {@link EndOfMessageFooter}.
*
* @return the footer
*/
public Footer getFooter() {
return footer;
}
private void setFooter(Footer footer) {
if (footer == null) throw new IllegalArgumentException("Footer must be specified");
this.footer = footer;
}
}