/* * Copyright (c) 2011-2013 The original author or authors * ------------------------------------------------------ * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.core.net.impl; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; import io.vertx.core.impl.ContextImpl; /** * @author <a href="mailto:nmaurer@redhat.com">Norman Maurer</a> */ public abstract class VertxHandler<C extends ConnectionBase> extends ChannelDuplexHandler { protected abstract C getConnection(); protected abstract C removeConnection(); protected ContextImpl getContext(C connection) { return connection.getContext(); } public static ByteBuf safeBuffer(ByteBuf buf, ByteBufAllocator allocator) { if (buf == Unpooled.EMPTY_BUFFER) { return buf; } if (buf.isDirect() || buf instanceof CompositeByteBuf) { try { if (buf.isReadable()) { ByteBuf buffer = allocator.heapBuffer(buf.readableBytes()); buffer.writeBytes(buf); return buffer; } else { return Unpooled.EMPTY_BUFFER; } } finally { buf.release(); } } return buf; } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { C conn = getConnection(); if (conn != null) { ContextImpl context = getContext(conn); context.executeFromIO(conn::handleInterestedOpsChanged); } } @Override public void exceptionCaught(ChannelHandlerContext chctx, final Throwable t) throws Exception { Channel ch = chctx.channel(); // Don't remove the connection at this point, or the handleClosed won't be called when channelInactive is called! C connection = getConnection(); if (connection != null) { ContextImpl context = getContext(connection); context.executeFromIO(() -> { try { if (ch.isOpen()) { ch.close(); } } catch (Throwable ignore) { } connection.handleException(t); }); } else { ch.close(); } } @Override public void channelInactive(ChannelHandlerContext chctx) throws Exception { C connection = removeConnection(); if (connection != null) { ContextImpl context = getContext(connection); context.executeFromIO(connection::handleClosed); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { C conn = getConnection(); if (conn != null) { ContextImpl context = getContext(conn); context.executeFromIO(conn::endReadAndFlush); } } @Override public void channelRead(ChannelHandlerContext chctx, Object msg) throws Exception { Object message = safeObject(msg, chctx.alloc()); C connection = getConnection(); ContextImpl context; if (connection != null) { context = getContext(connection); context.executeFromIO(connection::startRead); } else { context = null; } channelRead(connection, context, chctx, message); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state() == IdleState.ALL_IDLE) { ctx.close(); } ctx.fireUserEventTriggered(evt); } protected abstract void channelRead(C connection, ContextImpl context, ChannelHandlerContext chctx, Object msg) throws Exception; protected abstract Object safeObject(Object msg, ByteBufAllocator allocator) throws Exception; }