/* * Copyright 2019 wjybxx * * Licensed 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 iBn 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 com.wjybxx.fastjgame.net.session; import com.wjybxx.fastjgame.net.eventloop.NetEventLoop; import com.wjybxx.fastjgame.utils.concurrent.EventLoop; import com.wjybxx.fastjgame.utils.timer.TimerSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * {@link SessionHandlerContext}的模板实现。 * 它并不像netty的{@link io.netty.channel.ChannelHandlerContext}那么复杂。 * Q: 为什么可以简化? * A: 因为保证了{@link SessionPipeline}的所有{@link SessionHandler}都在同一个线程中执行,且取消了动态的组合、 * * @author wjybxx * @version 1.0 * date - 2019/9/25 * github - https://github.com/hl845740757 */ abstract class AbstractSessionHandlerContext implements SessionHandlerContext { private static final Logger logger = LoggerFactory.getLogger(AbstractSessionHandlerContext.class); private final DefaultSessionPipeline pipeline; /** * 上一个handlerContext */ AbstractSessionHandlerContext prev; /** * 下一个handlerContext */ AbstractSessionHandlerContext next; /** * 最新版的Netty,这个已经进行了优化,判断handler重写了哪些方法,然后直接找到下一个重写了指定方法的handler, * 不但可以有更短的事件流,而且可以大幅减少匿名内部类对象(lambda表达式也是一样)。 * 我们暂时不必如此。 */ private boolean isInbound; private boolean isOutbound; AbstractSessionHandlerContext(DefaultSessionPipeline pipeline) { this.pipeline = pipeline; } @Override public Session session() { return pipeline.session(); } @Override public SessionPipeline pipeline() { return pipeline; } @Override public NetEventLoop netEventLoop() { return pipeline.netEventLoop(); } @Override public EventLoop appEventLoop() { return pipeline.appEventLoop(); } @Override public TimerSystem timerSystem() { return pipeline.timerSystem(); } @Override public void handlerAdded() { isInbound = handler() instanceof SessionInboundHandler; isOutbound = handler() instanceof SessionOutboundHandler; try { handler().handlerAdded(this); } catch (Throwable e) { onExceptionCaught(e, this); } } @Override public void handlerRemoved() { try { handler().handlerRemoved(this); } catch (Throwable e) { onExceptionCaught(e, this); } } @Override public void tick() { try { handler().tick(this); } catch (Throwable e) { onExceptionCaught(e, this); } } // --------------------------------------------------- inbound ---------------------------------------------- @Override public void fireSessionActive() { final AbstractSessionHandlerContext nextInboundContext = findNextInboundContext(); invokeSessionActive(nextInboundContext); } private static void invokeSessionActive(AbstractSessionHandlerContext nextInboundContext) { final SessionInboundHandler handler = (SessionInboundHandler) nextInboundContext.handler(); try { handler.onSessionActive(nextInboundContext); } catch (Throwable e) { onExceptionCaught(e, nextInboundContext); } } @Override public void fireSessionInactive() { final AbstractSessionHandlerContext nextInboundContext = findNextInboundContext(); invokeSessionInactive(nextInboundContext); } private static void invokeSessionInactive(AbstractSessionHandlerContext nextInboundContext) { final SessionInboundHandler handler = (SessionInboundHandler) nextInboundContext.handler(); try { handler.onSessionInactive(nextInboundContext); } catch (Throwable e) { onExceptionCaught(e, nextInboundContext); } } @Override public void fireRead(@Nonnull Object msg) { final AbstractSessionHandlerContext nextInboundContext = findNextInboundContext(); invokeRead(msg, nextInboundContext); } private static void invokeRead(@Nullable Object message, AbstractSessionHandlerContext nextInboundContext) { final SessionInboundHandler handler = (SessionInboundHandler) nextInboundContext.handler(); try { handler.read(nextInboundContext, message); } catch (Throwable e) { onExceptionCaught(e, nextInboundContext); } } @Override public void fireExceptionCaught(Throwable cause) { final AbstractSessionHandlerContext nextInboundContext = findNextInboundContext(); invokeExceptionCaught(cause, nextInboundContext); } private static void invokeExceptionCaught(Throwable cause, AbstractSessionHandlerContext nextInboundContext) { final SessionInboundHandler handler = (SessionInboundHandler) nextInboundContext.handler(); try { handler.onExceptionCaught(nextInboundContext, cause); } catch (Throwable e) { logger.warn("An exception was thrown by a user handler while handling an exceptionCaught event", e); } } private static void onExceptionCaught(Throwable cause, AbstractSessionHandlerContext curContext) { if (curContext.isInbound) { invokeExceptionCaught(cause, curContext); } else { invokeExceptionCaught(cause, curContext.findNextInboundContext()); } } // --------------------------------------------------- outbound ---------------------------------------------- @Override public void fireWrite(@Nonnull Object msg) { final AbstractSessionHandlerContext nextOutboundContext = findNextOutboundContext(); invokeWrite(msg, nextOutboundContext); } private static void invokeWrite(@Nonnull Object msg, AbstractSessionHandlerContext nextOutboundContext) { final SessionOutboundHandler handler = (SessionOutboundHandler) nextOutboundContext.handler(); try { handler.write(nextOutboundContext, msg); } catch (Throwable e) { onExceptionCaught(e, nextOutboundContext); } } @Override public void fireFlush() { final AbstractSessionHandlerContext nextOutboundContext = findNextOutboundContext(); invokeFlush(nextOutboundContext); } private static void invokeFlush(AbstractSessionHandlerContext nextOutboundContext) { final SessionOutboundHandler handler = (SessionOutboundHandler) nextOutboundContext.handler(); try { handler.flush(nextOutboundContext); } catch (Throwable e) { onExceptionCaught(e, nextOutboundContext); } } @Override public void fireWriteAndFlush(@Nonnull Object msg) { final AbstractSessionHandlerContext nextOutboundContext = findNextOutboundContext(); invokeWrite(msg, nextOutboundContext); invokeFlush(nextOutboundContext); } @Override public void fireClose() { final AbstractSessionHandlerContext nextOutboundContext = findNextOutboundContext(); invokeClose(nextOutboundContext); } private void invokeClose(AbstractSessionHandlerContext nextOutboundContext) { final SessionOutboundHandler handler = (SessionOutboundHandler) nextOutboundContext.handler(); try { handler.close(nextOutboundContext); } catch (Throwable e) { onExceptionCaught(e, nextOutboundContext); } } /** * 寻找下一个入站处理器 (头部到尾部) - tail必须实现inboundHandler; * 此外:由于异常只有inboundHandler有,因此head也必须事件inboundHandler * * @return ctx */ @Nonnull private AbstractSessionHandlerContext findNextInboundContext() { AbstractSessionHandlerContext ctx = this; do { ctx = ctx.next; } while (!ctx.isInbound); return ctx; } /** * 寻找下一个出站处理器 (尾部到头部) - head必须实现outboundHandler。 * * @return ctx */ @Nonnull private AbstractSessionHandlerContext findNextOutboundContext() { AbstractSessionHandlerContext ctx = this; do { ctx = ctx.prev; } while (!ctx.isOutbound); return ctx; } }