/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.channel;
import io.netty.bootstrap.
Bootstrap;
import io.netty.bootstrap.
ServerBootstrap;
import io.netty.channel.
ChannelHandler.
Sharable;
import io.netty.util.internal.
PlatformDependent;
import io.netty.util.internal.logging.
InternalLogger;
import io.netty.util.internal.logging.
InternalLoggerFactory;
import java.util.concurrent.
ConcurrentMap;
/**
* A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was
* registered to its {@link EventLoop}.
*
* Implementations are most often used in the context of {@link Bootstrap#handler(ChannelHandler)} ,
* {@link ServerBootstrap#handler(ChannelHandler)} and {@link ServerBootstrap#childHandler(ChannelHandler)} to
* setup the {@link ChannelPipeline} of a {@link Channel}.
*
* <pre>
*
* public class MyChannelInitializer extends {@link ChannelInitializer} {
* public void initChannel({@link Channel} channel) {
* channel.pipeline().addLast("myHandler", new MyHandler());
* }
* }
*
* {@link ServerBootstrap} bootstrap = ...;
* ...
* bootstrap.childHandler(new MyChannelInitializer());
* ...
* </pre>
* Be aware that this class is marked as {@link Sharable} and so the implementation must be safe to be re-used.
*
* @param <C> A sub-type of {@link Channel}
*/
@
Sharable
public abstract class
ChannelInitializer<C extends
Channel> extends
ChannelInboundHandlerAdapter {
private static final
InternalLogger logger =
InternalLoggerFactory.
getInstance(
ChannelInitializer.class);
// We use a ConcurrentMap as a ChannelInitializer is usually shared between all Channels in a Bootstrap /
// ServerBootstrap. This way we can reduce the memory usage compared to use Attributes.
private final
ConcurrentMap<
ChannelHandlerContext,
Boolean>
initMap =
PlatformDependent.
newConcurrentHashMap();
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
*
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
* the {@link Channel}.
*/
protected abstract void
initChannel(C
ch) throws
Exception;
@
Override
@
SuppressWarnings("unchecked")
public final void
channelRegistered(
ChannelHandlerContext ctx) throws
Exception {
// Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
// the handler.
if (
initChannel(
ctx)) {
// we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
// miss an event.
ctx.
pipeline().
fireChannelRegistered();
} else {
// Called initChannel(...) before which is the expected behavior, so just forward the event.
ctx.
fireChannelRegistered();
}
}
/**
* Handle the {@link Throwable} by logging and closing the {@link Channel}. Sub-classes may override this.
*/
@
Override
public void
exceptionCaught(
ChannelHandlerContext ctx,
Throwable cause) throws
Exception {
if (
logger.
isWarnEnabled()) {
logger.
warn("Failed to initialize a channel. Closing: " +
ctx.
channel(),
cause);
}
ctx.
close();
}
/**
* {@inheritDoc} If override this method ensure you call super!
*/
@
Override
public void
handlerAdded(
ChannelHandlerContext ctx) throws
Exception {
if (
ctx.
channel().
isRegistered()) {
// This should always be true with our current DefaultChannelPipeline implementation.
// The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
// surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
// will be added in the expected order.
initChannel(
ctx);
}
}
@
SuppressWarnings("unchecked")
private boolean
initChannel(
ChannelHandlerContext ctx) throws
Exception {
if (
initMap.
putIfAbsent(
ctx,
Boolean.
TRUE) == null) { // Guard against re-entrance.
try {
initChannel((C)
ctx.
channel());
} catch (
Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(
ctx,
cause);
} finally {
remove(
ctx);
}
return true;
}
return false;
}
private void
remove(
ChannelHandlerContext ctx) {
try {
ChannelPipeline pipeline =
ctx.
pipeline();
if (
pipeline.
context(this) != null) {
pipeline.
remove(this);
}
} finally {
initMap.
remove(
ctx);
}
}
}