/**
*
*/
package vnet.sms.gateway.server.framework.internal.channel;
import static org.apache.commons.lang.Validate.notEmpty;
import static org.apache.commons.lang.Validate.notNull;
import java.io.Serializable;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
import org.jboss.netty.util.Timer;
import org.springframework.jmx.export.MBeanExportOperations;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.security.authentication.AuthenticationManager;
import vnet.sms.gateway.nettysupport.logging.incoming.ChannelContextLoggingUpstreamChannelHandler;
import vnet.sms.gateway.nettysupport.login.incoming.IncomingLoginRequestsChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.ChannelInfoChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.incoming.IncomingBytesCountingChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.incoming.IncomingMessagesMonitoringChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.incoming.IncomingPdusCountingChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.incoming.InitialChannelEventsMonitor;
import vnet.sms.gateway.nettysupport.monitor.incoming.InitialChannelEventsPublishingUpstreamChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.outgoing.OutgoingBytesCountingChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.outgoing.OutgoingMessagesMonitoringChannelHandler;
import vnet.sms.gateway.nettysupport.monitor.outgoing.OutgoingPdusCountingChannelHandler;
import vnet.sms.gateway.nettysupport.ping.outgoing.OutgoingPingChannelHandler;
import vnet.sms.gateway.nettysupport.publish.incoming.IncomingMessagesListener;
import vnet.sms.gateway.nettysupport.publish.incoming.IncomingMessagesPublishingChannelHandler;
import vnet.sms.gateway.nettysupport.shutdown.ConnectedChannelsTrackingChannelHandler;
import vnet.sms.gateway.nettysupport.transport.incoming.TransportProtocolAdaptingUpstreamChannelHandler;
import vnet.sms.gateway.nettysupport.transport.outgoing.TransportProtocolAdaptingDownstreamChannelHandler;
import vnet.sms.gateway.nettysupport.window.WindowingChannelHandler;
import vnet.sms.gateway.nettysupport.window.incoming.IncomingWindowStore;
import vnet.sms.gateway.nettysupport.window.spi.MessageReferenceGenerator;
import vnet.sms.gateway.server.framework.Jmx;
import vnet.sms.gateway.server.framework.internal.jmsbridge.IncomingMessagesForwardingJmsBridge;
import com.yammer.metrics.core.MetricsRegistry;
/**
* @author obergner
*
*/
@ManagedResource(objectName = GatewayServerChannelPipelineFactory.OBJECT_NAME, description = "Netty ChannelPipelineFactory for attaching a pipeline of channel handlers to each newly connected channel")
public class GatewayServerChannelPipelineFactory<ID extends Serializable, TP>
implements ChannelPipelineFactory {
private static final String TYPE = "ChannelPipelineFactory";
private static final String NAME = "DEFAULT";
static final String OBJECT_NAME = Jmx.GROUP
+ ":type="
+ TYPE
+ ",name="
+ NAME;
private final int availableIncomingWindows;
private final long incomingWindowWaitTimeMillis;
private final long failedLoginResponseDelayMillis;
private final int pingIntervalSeconds;
private final long pingResponseTimeoutMillis;
private final Class<TP> pduType;
private final FrameDecoder frameDecoder;
private final OneToOneDecoder decoder;
private final OneToOneEncoder encoder;
private final TransportProtocolAdaptingUpstreamChannelHandler<ID, TP> upstreamTransportProtocolAdapter;
private final TransportProtocolAdaptingDownstreamChannelHandler<ID, TP> downstreamTransportProtocolAdapter;
private final AuthenticationManager authenticationManager;
private final MessageReferenceGenerator<ID> windowIdGenerator;
private final MBeanExportOperations mbeanExporter;
private final ConnectedChannelsTrackingChannelHandler connectedChannelsTracker;
private final InitialChannelEventsPublishingUpstreamChannelHandler initialChannelEventsHandler;
private final MetricsRegistry metricsRegistry;
private final Timer timer;
private final IncomingMessagesPublishingChannelHandler<ID> incomingMessagesPublisher = new IncomingMessagesPublishingChannelHandler<ID>();
private final ChannelContextLoggingUpstreamChannelHandler channelContextLoggingUpstreamHandler = new ChannelContextLoggingUpstreamChannelHandler();
public GatewayServerChannelPipelineFactory(
final String gatewayServerInstanceId,
final Class<TP> pduType,
final FrameDecoder frameDecoder,
final OneToOneDecoder decoder,
final OneToOneEncoder encoder,
final TransportProtocolAdaptingUpstreamChannelHandler<ID, TP> upstreamTransportProtocolAdapter,
final TransportProtocolAdaptingDownstreamChannelHandler<ID, TP> downstreamTransportProtocolAdapter,
final IncomingMessagesForwardingJmsBridge<ID> messageForwardingJmsBridge,
final int availableIncomingWindows,
final long incomingWindowWaitTimeMillis,
final AuthenticationManager authenticationManager,
final long failedLoginResponseDelayMillis,
final MessageReferenceGenerator<ID> windowIdGenerator,
final int pingIntervalSeconds,
final long pingResponseTimeoutMillis,
final MBeanExportOperations mbeanExporter,
final InitialChannelEventsMonitor initialChannelEventsMonitor,
final MetricsRegistry metricsRegistry, final Timer timer,
final ChannelGroup allConnectedChannels) {
notEmpty(gatewayServerInstanceId,
"Argument 'gatewayServerInstanceId' must neither be null nor empty");
notNull(pduType, "Argument 'pduType' must not be null");
notNull(frameDecoder, "Argument 'frameDecoder' must not be null");
notNull(encoder, "Argument 'encoder' must not be null");
notNull(upstreamTransportProtocolAdapter,
"Argument 'upstreamTransportProtocolAdapter' must not be null");
notNull(downstreamTransportProtocolAdapter,
"Argument 'downstreamTransportProtocolAdapter' must not be null");
notNull(messageForwardingJmsBridge,
"Argument 'messageForwardingJmsBridge' must not be null");
notNull(authenticationManager,
"Argument 'authenticationManager' must not be null");
notNull(windowIdGenerator,
"Argument 'windowIdGenerator' must not be null");
notNull(mbeanExporter, "Argument 'mbeanExporter' must not be null");
notNull(initialChannelEventsMonitor,
"Argument 'intialChannelEventsMonitor' must not be null");
notNull(metricsRegistry, "Argument 'metricsRegistry' must not be null");
notNull(timer, "Argument 'timer' must not be null");
notNull(allConnectedChannels,
"Argument 'allConnectedChannels' must not be null");
this.pduType = pduType;
this.frameDecoder = frameDecoder;
this.decoder = decoder;
this.encoder = encoder;
this.upstreamTransportProtocolAdapter = upstreamTransportProtocolAdapter;
this.downstreamTransportProtocolAdapter = downstreamTransportProtocolAdapter;
this.availableIncomingWindows = availableIncomingWindows;
this.incomingWindowWaitTimeMillis = incomingWindowWaitTimeMillis;
this.authenticationManager = authenticationManager;
this.failedLoginResponseDelayMillis = failedLoginResponseDelayMillis;
this.windowIdGenerator = windowIdGenerator;
this.pingIntervalSeconds = pingIntervalSeconds;
this.pingResponseTimeoutMillis = pingResponseTimeoutMillis;
this.mbeanExporter = mbeanExporter;
this.metricsRegistry = metricsRegistry;
this.timer = timer;
this.initialChannelEventsHandler = new InitialChannelEventsPublishingUpstreamChannelHandler(
initialChannelEventsMonitor);
this.connectedChannelsTracker = new ConnectedChannelsTrackingChannelHandler(
allConnectedChannels);
this.incomingMessagesPublisher.addListener(messageForwardingJmsBridge);
}
/**
* @see org.jboss.netty.channel.ChannelPipelineFactory#getPipeline()
*/
@Override
public ChannelPipeline getPipeline() throws Exception {
final ChannelPipeline pipeline = Channels.pipeline();
// Push current channel onto MDC so that we may log it
pipeline.addLast(ChannelContextLoggingUpstreamChannelHandler.NAME,
this.channelContextLoggingUpstreamHandler);
// Publish OPEN, BOUND and CONNECTED events
pipeline.addLast(
InitialChannelEventsPublishingUpstreamChannelHandler.NAME,
this.initialChannelEventsHandler);
// Track connected channels
pipeline.addLast(ConnectedChannelsTrackingChannelHandler.NAME,
this.connectedChannelsTracker);
// Publish general channel attributes: connected-since, id, ...
pipeline.addLast(ChannelInfoChannelHandler.NAME,
new ChannelInfoChannelHandler(this.metricsRegistry));
// Monitor number of incoming bytes ...
pipeline.addLast(IncomingBytesCountingChannelHandler.NAME,
new IncomingBytesCountingChannelHandler(this.metricsRegistry));
// ... and outgoing bytes, too, while we are at it.
pipeline.addLast(OutgoingBytesCountingChannelHandler.NAME,
new OutgoingBytesCountingChannelHandler(this.metricsRegistry));
// Frame decoder, decoder, encoder
pipeline.addLast("vnet.sms.gateway:frame-decoder", this.frameDecoder);
// May be null in case of object serialization where the decoder IS the
// frame decoder
if (this.decoder != null) {
pipeline.addLast("vnet.sms.gateway:decoder", this.decoder);
}
pipeline.addLast("vnet.sms.gateway:encoder", this.encoder);
// Monitor number of incoming PDUs ...
pipeline.addLast(IncomingPdusCountingChannelHandler.NAME,
new IncomingPdusCountingChannelHandler<TP>(this.pduType,
this.metricsRegistry));
// ... and outgoing PDUs, too, while we are at it.
pipeline.addLast(OutgoingPdusCountingChannelHandler.NAME,
new OutgoingPdusCountingChannelHandler<TP>(this.pduType,
this.metricsRegistry));
// Transport protocol adapter
pipeline.addLast(TransportProtocolAdaptingUpstreamChannelHandler.NAME,
this.upstreamTransportProtocolAdapter);
pipeline.addLast(
TransportProtocolAdaptingDownstreamChannelHandler.NAME,
this.downstreamTransportProtocolAdapter);
// Monitor incoming and outgoing messages
pipeline.addLast(IncomingMessagesMonitoringChannelHandler.NAME,
new IncomingMessagesMonitoringChannelHandler<ID>(
this.metricsRegistry));
pipeline.addLast(OutgoingMessagesMonitoringChannelHandler.NAME,
new OutgoingMessagesMonitoringChannelHandler<ID>(
this.metricsRegistry));
// Windowing channel handler
pipeline.addLast(WindowingChannelHandler.NAME,
new WindowingChannelHandler<Serializable>(
new IncomingWindowStore<Serializable>(
this.availableIncomingWindows,
this.incomingWindowWaitTimeMillis),
this.metricsRegistry));
// Login channel handler
pipeline.addLast(IncomingLoginRequestsChannelHandler.NAME,
new IncomingLoginRequestsChannelHandler<ID>(
this.authenticationManager,
this.failedLoginResponseDelayMillis, this.timer));
// Ping channel handler
pipeline.addLast(OutgoingPingChannelHandler.NAME,
new OutgoingPingChannelHandler<ID>(this.pingIntervalSeconds,
this.pingResponseTimeoutMillis, this.windowIdGenerator,
this.timer, this.timer));
// Publish incoming messages to interested parties
pipeline.addLast(IncomingMessagesPublishingChannelHandler.NAME,
this.incomingMessagesPublisher);
return pipeline;
}
public void addListener(final IncomingMessagesListener<ID> listener) {
this.incomingMessagesPublisher.addListener(listener);
}
public void removeListener(final IncomingMessagesListener<ID> listener) {
this.incomingMessagesPublisher.removeListener(listener);
}
public void clearListeners() {
this.incomingMessagesPublisher.clearListeners();
}
/**
* @return the availableIncomingWindows
*/
@ManagedAttribute(description = "Number of available windows for incoming messages. Once these are exhausted, a connected "
+ "client needs to wait for acknowledgements for all sent messages before being allowed to send further messages.")
public int getAvailableIncomingWindows() {
return this.availableIncomingWindows;
}
/**
* @return the incomingWindowWaitTimeMillis
*/
@ManagedAttribute(description = "If no free window is available when processing an incoming message, we wait up to this time "
+ "span in milliseconds for a window to become available. If this fails, we discard that message.")
public long getIncomingWindowWaitTimeMillis() {
return this.incomingWindowWaitTimeMillis;
}
/**
* @return the failedLoginResponseDelayMillis
*/
@ManagedAttribute(description = "To prevent denial of service (DoS) attacks, we delay our response to a failed login attempt for this number of milliseconds.")
public long getFailedLoginResponseDelayMillis() {
return this.failedLoginResponseDelayMillis;
}
/**
* @return the pingIntervalSeconds
*/
@ManagedAttribute(description = "The interval in seconds between two pings we send out to clients.")
public int getPingIntervalSeconds() {
return this.pingIntervalSeconds;
}
/**
* @return the pingResponseTimeoutMillis
*/
@ManagedAttribute(description = "After sending a ping to a connected client we wait for up to this number of milliseconds "
+ "before we consider the ping as failed and close this channel.")
public long getPingResponseTimeoutMillis() {
return this.pingResponseTimeoutMillis;
}
public ChannelGroup getAllConnectedChannels() {
return this.connectedChannelsTracker.getAllConnectedChannels();
}
}