/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.server.services.session;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Resource;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanServerConnection;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import org.apache.log4j.Logger;
import org.helios.apmrouter.catalog.DChannelEvent;
import org.helios.apmrouter.catalog.DChannelEventMBean;
import org.helios.apmrouter.catalog.MetricCatalogService;
import org.helios.apmrouter.jmx.JMXHelper;
import org.helios.apmrouter.jmx.ThreadPoolFactory;
import org.helios.apmrouter.jmx.mbeanserver.AgentMBeanServerConnectionFactory;
import org.helios.apmrouter.server.services.session.ChannelSessionEvent.ChannelSessionStartedEvent;
import org.helios.apmrouter.server.services.session.ChannelSessionEvent.ChannelSessionStoppedEvent;
import org.helios.apmrouter.util.SystemClock;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.ChannelGroupFuture;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* <p>Title: SharedChannelGroup</p>
* <p>Description: A common netty channel group for tracking all client and server channels that supports decorated channels.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.server.services.session.SharedChannelGroup</code></p>
*/
public class SharedChannelGroup implements ChannelGroup, ChannelFutureListener, ApplicationContextAware, SharedChannelGroupMXBean, NotificationBroadcaster {
/** The singleton instance */
private static volatile SharedChannelGroup instance = null;
/** The singleton instance ctor lock */
private static final Object lock = new Object();
/** Instance logger */
protected final Logger log = Logger.getLogger(getClass());
/** Thread pool for dispatchng events and notifications */
protected final ThreadPoolExecutor threadPool;
/** The JMX notification support delegate */
protected final NotificationBroadcasterSupport notificationBroadcaster;
/** The catalog service that sorts out all the session events */
protected MetricCatalogService mcs = null;
/** The application context */
protected ApplicationContext applicationContext = null;
/** A map of sets of channels keyed by their channel type */
protected final Map<ChannelType, Set<DecoratedChannel>> channelsByType = Collections.synchronizedMap(new EnumMap<ChannelType, Set<DecoratedChannel>>(ChannelType.class));
/** A map of remotely connected channels keyed by remote socket address */
protected final Map<SocketAddress, DecoratedChannel> channelsByRemote = new ConcurrentHashMap<SocketAddress, DecoratedChannel>();
/** A map of remotely connected channels keyed by host/agent */
protected final Map<String, DecoratedChannel> channelsByHostAgent = new ConcurrentHashMap<String, DecoratedChannel>();
/** The core delegate channel group */
protected ChannelGroup channelGroup = new DefaultChannelGroup("APMRouterChannelGroup");
/** A set of registered channel session listeners */
protected final Set<ChannelSessionListener> listeners = new CopyOnWriteArraySet<ChannelSessionListener>();
/** The pring scheduler for sessions that need to be pinged */
protected final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, new ThreadFactory(){
private final AtomicInteger serial = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "SharedChannelGroupPingerThread#" + serial.incrementAndGet());
t.setDaemon(true);
return t;
}
});
/** Serial number generator for jmx notifications */
protected final AtomicLong jmxNotifSerial = new AtomicLong(0L);
/**
* Retrieves the SharedChannelGroup singleton instance
* @return the SharedChannelGroup singleton instance
*/
public static SharedChannelGroup getInstance() {
if(instance==null) {
synchronized(lock) {
if(instance==null) {
instance = new SharedChannelGroup();
}
}
}
return instance;
}
/**
* Builds the SharedChannelGroup singleton instance
* @param mcs the MetricCatalogService to init with
* @return the SharedChannelGroup singleton instance
*/
public static SharedChannelGroup getInstance(MetricCatalogService mcs) {
if(instance==null) {
synchronized(lock) {
if(instance==null) {
instance = new SharedChannelGroup();
instance.setMetricCatalogService(mcs);
}
}
}
return instance;
}
/**
* Creates a new SharedChannelGroup
*/
private SharedChannelGroup() {
for(ChannelType type: ChannelType.values()) {
channelsByType.put(type, new CopyOnWriteArraySet<DecoratedChannel>());
}
threadPool = (ThreadPoolExecutor) ThreadPoolFactory.newCachedThreadPool("org.helios.apmrouter.session", "SharedChannelGroupPool");
notificationBroadcaster = new NotificationBroadcasterSupport(threadPool, new MBeanNotificationInfo[]{
new MBeanNotificationInfo(new String[]{NEW_SESSION_EVENT}, Notification.class.getName(), "Notification broadcast when a channel session is initiated"),
new MBeanNotificationInfo(new String[]{CLOSED_SESSION_EVENT}, Notification.class.getName(), "Notification broadcast when a channel session is closed"),
new MBeanNotificationInfo(new String[]{IDENTIFIED_SESSION_EVENT}, Notification.class.getName(), "Notification broadcast when a channel is identified"),
new MBeanNotificationInfo(new String[]{HOST_UP_EVENT}, Notification.class.getName(), "Notification broadcast when a host comes up"),
new MBeanNotificationInfo(new String[]{HOST_DOWN_EVENT}, Notification.class.getName(), "Notification broadcast when a host goes down")
});
JMXHelper.registerMBean(OBJECT_NAME, this);
scheduler.scheduleAtFixedRate(new Runnable(){
public void run() {
for(DecoratedChannel dc: channelsByRemote.values()) {
//=======================================================================================
// FIXME: This is temporary until:
// A. We can handle the full agent protocol in JS WebSock Clients
// B. We have a cleaner and more specific way of excluding JS WebSock Clients
// since agent protocol capable clients may be using WebSock
//=======================================================================================
if((dc.host==null || dc.agent==null) && dc.getChannelType()!=ChannelType.WEBSOCKET_REMOTE) {
dc.sendWho();
}
}
}
}, 3000, 3000, TimeUnit.MILLISECONDS);
}
/**
* Returns an array of the decorated channels
* @return an array of the decorated channels
*/
public DecoratedChannelMBean[] getChannels() {
return toArray(new DecoratedChannelMBean[size()]);
}
/**
* Registers a new session listener
* @param listener the listener to register
*/
public void addSessionListener(ChannelSessionListener listener) {
if(listener!=null) {
listeners.add(listener);
}
}
/**
* Removes a registered session listener
* @param listener the listener to remove
*/
public void removeSessionListener(ChannelSessionListener listener) {
if(listener!=null) {
listeners.remove(listener);
}
}
/**
* ChannelFutureListener impl that removes Channels from the group when they close.
* {@inheritDoc}
* @see org.jboss.netty.channel.ChannelFutureListener#operationComplete(org.jboss.netty.channel.ChannelFuture)
*/
@Override
public void operationComplete(ChannelFuture f) throws Exception {
if(f.isDone()) {
remove(f.getChannel());
}
}
/**
* Returns the channel connected to the passed remote address
* @param sa the remote address
* @return a channel or null if one was not found
*/
public Channel getByRemote(SocketAddress sa) {
if(sa==null) return null;
return channelsByRemote.get(sa);
}
/**
* Converts the passed channel to a {@link DecoratedChannel} and adds it to the channel group
* @param channel The channel to add
* @param type The channel type
* @param name The assigned channel name
* @return true if the channel was not already registered in this channel group, false otherwise
*/
public boolean add(Channel channel, ChannelType type, String name) {
return add((channel instanceof DecoratedChannel) ? channel : new DecoratedChannel(channel, type, name));
}
/**
* Converts the passed channel to a {@link DecoratedChannel} and adds it to the channel group
* @param channel The channel to add
* @param type The channel type
* @param name The assigned channel name
* @return true if the channel was not already registered in this channel group, false otherwise
*/
public boolean add(Channel channel, ChannelType type, String name, String host, String agent) {
DecoratedChannel dc = (channel instanceof DecoratedChannel) ? (DecoratedChannel)channel : new DecoratedChannel(channel, type, name);
boolean b = add(dc);
dc.setWho(host, agent);
sendIdentifiedChannelEvent(dc);
return b;
}
/**
* {@inheritDoc}
* @see java.util.Set#add(java.lang.Object)
*/
@Override
public boolean add(Channel channel) {
final DecoratedChannel dchannel;
if(channel instanceof DecoratedChannel) {
dchannel = (DecoratedChannel)channel;
} else {
dchannel = new DecoratedChannel(channel, ChannelType.OTHER, channel.getClass().getSimpleName());
}
boolean isNew = channelGroup.add(dchannel);
if(isNew) {
SocketAddress remote = dchannel.getRemoteAddress();
if(remote!=null && !channelsByRemote.containsKey(remote)) {
channelsByRemote.put(dchannel.getRemoteAddress(), dchannel);
}
channelsByType.get(dchannel.getChannelType()).add(dchannel);
dchannel.getCloseFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
remove(dchannel);
}
});
registerWithListeners(dchannel);
if(dchannel.getDelegate() instanceof VirtualUDPChannel) {
final VirtualUDPChannel vuc = (VirtualUDPChannel)dchannel.getDelegate();
vuc.setPingSchedule(
scheduler.scheduleAtFixedRate(new Runnable(){
public void run() {
vuc.ping();
}
}, 10000, 10000, TimeUnit.MILLISECONDS)
);
}
}
return isNew;
}
/**
* @param dchannel
* FIXME: MUTLITHREAD, EVENTS, NOTIFS, SEND NEW CHANNEL
*/
private void registerWithListeners(final DecoratedChannel dchannel) {
sendChannelConnectedEvent(dchannel);
final Set<ChannelSessionListener> forwardTo = new HashSet<ChannelSessionListener>();
ChannelFutureListener relay = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
final DChannelEventMBean dce = mcs.onClosedChannel(dchannel);
sendChannelClosedEvent(dchannel, dce);
for(final ChannelSessionListener listener: forwardTo) {
threadPool.submit(new Runnable() {
@Override
public void run() {
listener.onClosedChannel(dchannel);
}
});
}
}
};
for(final ChannelSessionListener listener: listeners) {
if(listener instanceof FilteredChannelSessionListener) {
if(((FilteredChannelSessionListener)listener).include(dchannel)) {
forwardTo.add(listener);
}
} else {
forwardTo.add(listener);
}
}
dchannel.getCloseFuture().addListener(relay);
}
// /**
// * Sends a host down event to jmx and spring
// * @param dchannel The closed channel that detected the last agent connection that closed
// */
// protected void sendHostDownEvent(final DecoratedChannelMBean dchannel) {
// Notification notif = new Notification(HOST_DOWN_EVENT, OBJECT_NAME, jmxNotifSerial.incrementAndGet(), SystemClock.time(), "Host Down Detected By [" + dchannel.toString() + "]");
// notif.setUserData(dchannelToJson("HD", dchannel));
// sendNotification(notif);
// if(applicationContext != null) {
// threadPool.submit(new Runnable() {
// @Override
// public void run() {
// applicationContext.publishEvent(new HostDownEvent(dchannel));
// }
// });
// }
// }
//
// /**
// * Sends a host up event to jmx and spring
// * @param dchannel The opened channel that detected the first agent connection that opened
// */
// protected void sendHostUpEvent(final DecoratedChannelMBean dchannel) {
// Notification notif = new Notification(HOST_UP_EVENT, OBJECT_NAME, jmxNotifSerial.incrementAndGet(), SystemClock.time(), "Host Up Detected By [" + dchannel.toString() + "]");
// notif.setUserData(dchannelToJson("HU", dchannel));
// sendNotification(notif);
// if(applicationContext != null) {
// threadPool.submit(new Runnable() {
// @Override
// public void run() {
// applicationContext.publishEvent(new HostUpEvent(dchannel));
// }
// });
// }
// }
//
/**
* Sends a channel closed event to jmx and spring
* @param dchannel The closed channel
* @param dce The channel state change event context
*/
protected void sendChannelClosedEvent(final DecoratedChannelMBean dchannel, DChannelEventMBean dce) {
Notification notif = new Notification(CLOSED_SESSION_EVENT, OBJECT_NAME, jmxNotifSerial.incrementAndGet(), SystemClock.time(), "Channel Session Closed [" + dchannel.toString() + "]");
notif.setUserData(dce);
sendNotification(notif);
if(applicationContext != null) {
threadPool.submit(new Runnable() {
@Override
public void run() {
applicationContext.publishEvent(new ChannelSessionStoppedEvent(dchannel));
}
});
}
}
/**
* Sends a channel connected event to listeners, jmx and spring
* @param dchannel The connected channel
*/
protected void sendChannelConnectedEvent(final DecoratedChannelMBean dchannel) {
for(final ChannelSessionListener listener: listeners) {
if(listener instanceof FilteredChannelSessionListener) {
if(((FilteredChannelSessionListener)listener).include(dchannel)) {
threadPool.submit(new Runnable() {
@Override
public void run() {
listener.onConnectedChannel((DecoratedChannel)dchannel);
}
});
}
} else {
threadPool.submit(new Runnable() {
@Override
public void run() {
listener.onConnectedChannel((DecoratedChannel)dchannel);
}
});
}
}
Notification notif = new Notification(NEW_SESSION_EVENT, OBJECT_NAME, jmxNotifSerial.incrementAndGet(), SystemClock.time(), "Channel Session Started [" + dchannel.toString() + "]");
sendNotification(notif);
if(applicationContext != null) {
threadPool.submit(new Runnable() {
@Override
public void run() {
applicationContext.publishEvent(new ChannelSessionStartedEvent(dchannel));
}
});
}
}
/**
* Builds a cache key from the decorated channel's protocol, agent and host
* @param dc The decorated channel to get the key for
* @return a cache key
*/
protected String makeKey(DecoratedChannel dc) {
return dc.getChannelType().protocol + "://" + dc.getAgent() + "@" + dc.getHost();
}
/**
* Returns a map of registered channels for the passed agent and host
* @param agent The agent
* @param host The host
* @return a [possibly empty] array of registered channels for the passed agent and host
*/
@Override
public DecoratedChannel[] findByAgentHost(String agent, String host) {
Map<String, DecoratedChannel> results = new HashMap<String, DecoratedChannel>();
final String suffix = agent + "@" + host;
for(Map.Entry<String, DecoratedChannel> entry: channelsByHostAgent.entrySet()) {
if(entry.getKey().endsWith(suffix)) {
results.put(entry.getValue().getChannelType().protocol, entry.getValue());
}
}
return results.values().toArray(new DecoratedChannel[results.size()]);
}
/**
* Acquires an {@link MBeanServerConnection} to the named agent
* @param agent The agent name
* @param host The agent's host
* @param domain The default domain of the target MBeanServer
* @return an {@link MBeanServerConnection} to the named agent
*/
public MBeanServerConnection getMBeanServerConnection(String agent, String host, String domain) {
DecoratedChannel[] dcs = findByAgentHost(agent, host);
if(dcs.length>0) {
DecoratedChannel dc = dcs[0];
return AgentMBeanServerConnectionFactory.builder(dc).domain(domain).remoteAddress(dc.getRemoteAddress()).build();
}
throw new RuntimeException("No channel found for [" + agent + "@" + host + "]", new Throwable());
}
/**
* Acquires an {@link MBeanServerConnection} to the named agent
* @param protocol The protocol to comm with
* @param agent The agent name
* @param host The agent's host
* @param domain The default domain of the target MBeanServer
* @return an {@link MBeanServerConnection} to the named agent
*/
public MBeanServerConnection getMBeanServerConnection(String protocol, String agent, String host, String domain) {
DecoratedChannel dc = findByAgentHost(protocol, agent, host);
if(dc!=null) {
return AgentMBeanServerConnectionFactory.builder(dc).domain(domain).remoteAddress(dc.getRemoteAddress()).build();
}
throw new RuntimeException("No channel found for [" + protocol + "://" + agent + "@" + host + "]", new Throwable());
}
/**
* Returns the registered channels for the passed protocol, agent and host
* @param protocol The protocol
* @param agent The agent
* @param host The host
* @return the registered channel for the passed protocol, agent and host keyed or null if one was not found
*/
@Override
public DecoratedChannel findByAgentHost(String protocol, String agent, String host) {
final String key = protocol + "://" + agent + "@" + host;
return channelsByHostAgent.get(key);
}
/**
* Sends a JMX notification indicating that a channel session has been identified.
* @param dchannel The channel that has been identified.
*/
public void sendIdentifiedChannelEvent(final DecoratedChannel dchannel) {
final String key = makeKey(dchannel);
channelsByHostAgent.put(key, dchannel);
dchannel.getCloseFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
channelsByHostAgent.remove(key);
}
});
final DChannelEventMBean dce = mcs.onIdentifiedChannel(dchannel);
for(final ChannelSessionListener listener: listeners) {
if(listener instanceof FilteredChannelSessionListener) {
if(((FilteredChannelSessionListener)listener).include(dchannel)) {
threadPool.submit(new Runnable() {
@Override
public void run() {
listener.onIdentifiedChannel(dchannel);
}
});
}
} else {
threadPool.submit(new Runnable() {
@Override
public void run() {
listener.onIdentifiedChannel(dchannel);
}
});
}
}
Notification notif = new Notification(IDENTIFIED_SESSION_EVENT, OBJECT_NAME, jmxNotifSerial.incrementAndGet(), SystemClock.time(), "Channel Session Identified [" + dchannel.host + "/" + dchannel.agent + "]");
notif.setUserData(dce);
sendNotification(notif);
}
/**
* Sends a notification to listeners indicating that a new virtual agent came on line
* @param dce The contrived {@link DChannelEvent} created for the virtual agent
*/
public void sendVirtualAgentStartedEvent(DChannelEvent dce) {
Notification notif = new Notification(IDENTIFIED_SESSION_EVENT, OBJECT_NAME, jmxNotifSerial.incrementAndGet(), SystemClock.time(), "Channel Session Identified [" + dce.host + "/" + dce.agent + "]");
notif.setUserData(dce);
sendNotification(notif);
}
/**
* Sends a notification to listeners indicating that a virtual agent expired
* @param dce The contrived {@link DChannelEvent} created for the expired virtual agent
*/
public void sendVirtualAgentExpiredEvent(DChannelEvent dce) {
Notification notif = new Notification(CLOSED_SESSION_EVENT, OBJECT_NAME, jmxNotifSerial.incrementAndGet(), SystemClock.time(), "Channel Session Closed [" + dce.host + "/" + dce.agent + "]");
notif.setUserData(dce);
sendNotification(notif);
}
/**
* Removes a channel from the ChannelGroup
* @param channel The channel to remove
* @return true if the channel was present and was removed
* @see java.util.Set#remove(java.lang.Object)
*/
public boolean remove(Channel channel) {
if(channel instanceof DecoratedChannel) {
DecoratedChannel dchannel = (DecoratedChannel)channel;
channelsByType.get(dchannel.getChannelType()).remove(dchannel);
SocketAddress sa = dchannel.getRemoteAddress();
if(sa!=null) {
channelsByRemote.remove(sa);
} else {
for(Iterator<DecoratedChannel> iter = channelsByRemote.values().iterator(); iter.hasNext();) {
if(iter.next().equals(dchannel)) {
iter.remove();
break;
}
}
}
return channelGroup.remove(dchannel);
}
return channelGroup.remove(channel);
}
/**
* {@inheritDoc}
* @see java.util.Set#remove(java.lang.Object)
*/
@Override
public boolean remove(Object obj) {
if(obj!=null && obj instanceof Channel) {
return remove((Channel)obj);
}
return false;
}
/**
* {@inheritDoc}
* @see java.util.Set#size()
*/
@Override
public int size() {
return channelGroup.size();
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.services.session.SharedChannelGroupMXBean#getSize()
*/
@Override
public int getSize() {
return size();
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.services.session.SharedChannelGroupMXBean#isEmpty()
*/
@Override
public boolean isEmpty() {
return channelGroup.isEmpty();
}
/**
* {@inheritDoc}
* @see java.util.Set#contains(java.lang.Object)
*/
@Override
public boolean contains(Object o) {
return channelGroup.contains(o);
}
/**
* {@inheritDoc}
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(ChannelGroup o) {
return channelGroup.compareTo(o);
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.services.session.SharedChannelGroupMXBean#getName()
*/
@Override
public String getName() {
return channelGroup.getName();
}
/**
* {@inheritDoc}
* @see org.jboss.netty.channel.group.ChannelGroup#find(java.lang.Integer)
*/
@Override
public Channel find(Integer id) {
return channelGroup.find(id);
}
/**
* {@inheritDoc}
* @see java.util.Set#iterator()
*/
@Override
public Iterator<Channel> iterator() {
return channelGroup.iterator();
}
/**
* {@inheritDoc}
* @see org.jboss.netty.channel.group.ChannelGroup#setInterestOps(int)
*/
@Override
public ChannelGroupFuture setInterestOps(int interestOps) {
return channelGroup.setInterestOps(interestOps);
}
/**
* {@inheritDoc}
* @see java.util.Set#toArray()
*/
@Override
public Object[] toArray() {
return channelGroup.toArray();
}
/**
* {@inheritDoc}
* @see org.jboss.netty.channel.group.ChannelGroup#setReadable(boolean)
*/
@Override
public ChannelGroupFuture setReadable(boolean readable) {
return channelGroup.setReadable(readable);
}
/**
* {@inheritDoc}
* @see org.jboss.netty.channel.group.ChannelGroup#write(java.lang.Object)
*/
@Override
public ChannelGroupFuture write(Object message) {
return channelGroup.write(message);
}
/**
* {@inheritDoc}
*/
@Override
public <T> T[] toArray(T[] a) {
return channelGroup.toArray(a);
}
/**
* {@inheritDoc}
* @see org.jboss.netty.channel.group.ChannelGroup#write(java.lang.Object, java.net.SocketAddress)
*/
@Override
public ChannelGroupFuture write(Object message, SocketAddress remoteAddress) {
return channelGroup.write(message, remoteAddress);
}
/**
* {@inheritDoc}
*/
@Override
public ChannelGroupFuture disconnect() {
return channelGroup.disconnect();
}
/**
* {@inheritDoc}
* @see org.jboss.netty.channel.group.ChannelGroup#unbind()
*/
@Override
public ChannelGroupFuture unbind() {
return channelGroup.unbind();
}
/**
* {@inheritDoc}
*/
@Override
public ChannelGroupFuture close() {
return channelGroup.close();
}
/**
* {@inheritDoc}
* @see java.util.Set#containsAll(java.util.Collection)
*/
@Override
public boolean containsAll(Collection<?> c) {
return channelGroup.containsAll(c);
}
/**
* {@inheritDoc}
* @see java.util.Set#addAll(java.util.Collection)
*/
@Override
public boolean addAll(Collection<? extends Channel> c) {
boolean changed = false;
for(Channel channel: c) {
boolean ch = add(channel);
if(ch) changed = true;
}
return changed;
}
/**
* {@inheritDoc}
* @see java.util.Set#retainAll(java.util.Collection)
*/
@Override
public boolean retainAll(Collection<?> c) {
boolean changed = false;
Set<Channel> remove = new HashSet<Channel>();
for(Iterator<Channel> iter = iterator(); iter.hasNext();) {
Channel channel = iter.next();
if(!c.contains(channel)) {
remove.add(channel);
}
}
for(Channel channel: remove) {
boolean ch = remove(channel);
if(ch) changed = true;
}
return changed;
}
/**
* {@inheritDoc}
* @see java.util.Set#removeAll(java.util.Collection)
*/
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
Set<Channel> remove = new HashSet<Channel>();
for(Iterator<Channel> iter = iterator(); iter.hasNext();) {
Channel channel = iter.next();
if(c.contains(channel)) {
remove.add(channel);
}
}
for(Channel channel: remove) {
boolean ch = remove(channel);
if(ch) changed = true;
}
return changed;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.services.session.SharedChannelGroupMXBean#clear()
*/
@Override
public void clear() {
for(Set<DecoratedChannel> set : channelsByType.values()) {
set.clear();
}
channelGroup.clear();
}
/**
* {@inheritDoc}
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
return channelGroup.equals(o);
}
/**
* {@inheritDoc}
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return channelGroup.hashCode();
}
// ==============================================================================
// Notification Broadcaster Support
// ==============================================================================
/**
* {@inheritDoc}
* @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
*/
@Override
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback) {
notificationBroadcaster.addNotificationListener(listener, filter,
handback);
}
/**
* {@inheritDoc}
* @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
*/
@Override
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
notificationBroadcaster.removeNotificationListener(listener);
}
/**
* Removes a notification listener registered with a specific filter
* @param listener The listener to remove
* @param filter The filter the listener was added with
* @param handback The handback the listener was added with
* @throws ListenerNotFoundException thrown if the passed listener was not registered
* @see javax.management.NotificationBroadcasterSupport#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
*/
public void removeNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback)
throws ListenerNotFoundException {
notificationBroadcaster.removeNotificationListener(listener, filter,
handback);
}
/**
* {@inheritDoc}
* @see javax.management.NotificationBroadcaster#getNotificationInfo()
*/
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
return notificationBroadcaster.getNotificationInfo();
}
/**
* Sends a notification
* @param notification the notification to send
* @see javax.management.NotificationBroadcasterSupport#sendNotification(javax.management.Notification)
*/
public void sendNotification(Notification notification) {
notificationBroadcaster.sendNotification(notification);
}
/**
* {@inheritDoc}
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Sets the metric catalog service
* @param metricCatalogService the metricCatalogService to set
*/
@Autowired(required=true)
public void setMetricCatalogService(MetricCatalogService metricCatalogService) {
this.mcs = metricCatalogService;
}
}