Netty 4 (A1) — AttributeMap с несколькими обработчиками

Я пытаюсь передать объект от одного обработчика другому, используя метод ChannelContext.attr():

private class TCPInitializer extends ChannelInitializer<SocketChannel>
{
    @Override
    public void initChannel(final SocketChannel ch) throws Exception
    {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast(new ReadTimeoutHandler(mIdleTimeout));
        pipeline.addLast(new HostMappingHandler<SyslogHandler>(mRegisteredClients,
                                                               mChannels));
        pipeline.addLast(new DelimiterBasedFrameDecoder(MAX_FRAME_SIZE,
                         Delimiters.lineDelimiter()));
        pipeline.addLast(new Dispatcher());
    }
}

HostMappingHandler — это класс шаблона, который сопоставляет хосты с данными:

public class HostMappingHandler<T> extends ChannelStateHandlerAdapter
{

    public static final AttributeKey<HostMappedObject> HOST_MAPPING =
        new AttributeKey<HostMappedObject>("HostMappingHandler.attr");


    private final ChannelGroup mChannels;
    private final Map<String, T> mMap;


    public HostMappingHandler(Map<String, T> registrations,
                              ChannelGroup channelGroup)
    {
        mChannels = channelGroup;
        mMap = registrations;
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception
    {
        SocketAddress addr = ctx.channel().remoteAddress();
        T mappedObj = null;

        if (addr instanceof InetSocketAddress)
        {
            String host = ((InetSocketAddress) addr).getHostName().toLowerCase();
            mappedObj = mMap.get(host);
        }

        if (mappedObj != null)
        {
            // Add the channel to the list so it can be easily removed if unregistered
            mChannels.add(ctx.channel());

            // Attach the host-mapped object
            ctx.attr(HOST_MAPPING).set(new HostMappedObject<T>(mappedObj));
        }
        else
        {
            log.debug("Bad host [" + addr + "]; aborting connection request");
            ctx.channel().close();
        }
        super.channelRegistered(ctx);
    }

    @Override
    public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception
    {
        // Find the parser for this host. If the host is no longer registered,
        // disconnect the client.
        if (ctx.channel().remoteAddress() instanceof InetSocketAddress)
        {
            InetSocketAddress addr = (InetSocketAddress) ctx.channel().remoteAddress();
            T handler = mMap.get(addr.getHostName().toLowerCase());

            if (handler == null)
            {
                log.debug("Host no longer registered");
                ctx.channel().close();
            }
            else
            {
                log.debug("Sanity Check: " + ctx.attr(HostMappingHandler.HOST_MAPPING).get());
            }
        }
        super.inboundBufferUpdated(ctx);
    }

    // ==================================================

    public static class HostMappedObject<C>
    {
        private final C mObj;

        public HostMappedObject(final C in)
        {
            mObj = in;
        }

        public C get()
        {
            return mObj;
        }
    }
}

А диспетчер на данный момент очень простой:

private static class Dispatcher extends ChannelInboundMessageHandlerAdapter<Object>
{
    @Override
    public void messageReceived(final ChannelHandlerContext ctx,
                                final Object msg)
            throws Exception
    {
        HostMappingHandler.HostMappedObject wrapper =
                ctx.attr(HostMappingHandler.HOST_MAPPING).get();

        log.debug("Received: " + wrapper);
    }
}

Между прочим, инициализатор и диспетчер являются частными классами объекта "сервер". Однако, когда я запускаю модульный тест, который регистрирует локальный хост и пытается подключиться к нему, он терпит неудачу, и мой вывод отладки показывает:

HostMappingHandler.inboundBufferUpdated: Sanity Check: test.comms.netty.HostMappingHandler$HostMappedObject@eb017e
Dispatcher.messageReceived: Received: null

Таким образом, сопоставление определенно сохраняется между регистрацией канала и получением входящего сообщения в классе HostMapper, и дальнейшее исследование в отладчике показывает, что карта атрибутов для контекста в Dispatcher действительно имеет запись — проблема в том, что значение равно NULL а не предмет.

Я пропустил что-то очевидное или это просто альфа-баг?

Редактировать:

На данный момент я обошел эту проблему, присоединив данные к контексту Dispatcher, заставив HostMappingHandler также взять класс для присоединения. Ошибка, по-видимому, заключается в том, что установка атрибута в ChannelHandlerContext обработчика приводит к тому, что атрибут появляется в другом обработчике с правильным ключом, но со значением NULL. Не зная желаемого рабочего процесса, ошибка заключается в том, что либо ключ вообще не должен отображаться, либо значение не должно быть нулевым.

public HostMappingHandler(final Map<String, T> registrations,
                          final ChannelGroup channelGroup,
                          final Class<? extends ChannelHandler> channelHandler)
{
    mChannels = channelGroup;
    mMap = registrations;
    mHandler = channelHandler;
}

//...

@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception
{
    //...
    // Attach the host-mapped object
     ctx.pipeline().context(mHandler).attr(HOST_MAPPING).set(new HostMappedObject<T>(mappedObj));
    //...
}

person mrusinak    schedule 20.08.2012    source источник


Ответы (1)


AttributeMap в ChannelHandlerContext привязан к контексту, и поэтому атрибут, который вы устанавливаете в одном контексте, не виден в другом контексте.

Обратите внимание, что Channel также реализует AttributeMap. Вы можете просто использовать карту атрибутов, привязанную к каналу:

ctx.channel().attr(...) ...;
person trustin    schedule 23.08.2012
comment
Спасибо! Я запутался, когда ключ появился в другом ChannelHandlerContext, просто с нулевым значением. - person mrusinak; 23.08.2012
comment
Спасибо, это решило мою проблему, а также исправило мою мысль о том, что ChannelHandlerContext предназначен только для текущего обработчика, а не для всех обработчиков. - person Kelvin Hu; 01.02.2013