Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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 org.apache.pulsar.common.api; import com.google.common.annotations.VisibleForTesting; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import io.netty.util.AbstractReferenceCounted; import io.netty.util.Recycler; import io.netty.util.Recycler.Handle; import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; /** * ByteBuf holder that contains 2 buffers. */ public final class ByteBufPair extends AbstractReferenceCounted { private ByteBuf b1; private ByteBuf b2; private final Handle<ByteBufPair> recyclerHandle; private static final Recycler<ByteBufPair> RECYCLER = new Recycler<ByteBufPair>() { @Override protected ByteBufPair newObject(Recycler.Handle<ByteBufPair> handle) { return new ByteBufPair(handle); } }; private ByteBufPair(Handle<ByteBufPair> recyclerHandle) { this.recyclerHandle = recyclerHandle; } /** * Get a new {@link ByteBufPair} from the pool and assign 2 buffers to it. * <p> * The buffers b1 and b2 lifecycles are now managed by the ByteBufPair: when the {@link ByteBufPair} is deallocated, * b1 and b2 will be released as well. * * @param b1 * @param b2 * @return */ public static ByteBufPair get(ByteBuf b1, ByteBuf b2) { ByteBufPair buf = RECYCLER.get(); buf.setRefCnt(1); buf.b1 = b1; buf.b2 = b2; return buf; } public ByteBuf getFirst() { return b1; } public ByteBuf getSecond() { return b2; } public int readableBytes() { return b1.readableBytes() + b2.readableBytes(); } /** * @return a single buffer with the content of both individual buffers */ @VisibleForTesting public static ByteBuf coalesce(ByteBufPair pair) { ByteBuf b = Unpooled.buffer(pair.readableBytes()); b.writeBytes(pair.b1, pair.b1.readerIndex(), pair.b1.readableBytes()); b.writeBytes(pair.b2, pair.b2.readerIndex(), pair.b2.readableBytes()); return b; } @Override protected void deallocate() { b1.release(); b2.release(); b1 = b2 = null; recyclerHandle.recycle(this); } @Override public ReferenceCounted touch(Object hint) { b1.touch(hint); b2.touch(hint); return this; } public static final Encoder ENCODER = new Encoder(); public static final CopyingEncoder COPYING_ENCODER = new CopyingEncoder(); @Sharable public static class Encoder extends ChannelOutboundHandlerAdapter { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof ByteBufPair) { ByteBufPair b = (ByteBufPair) msg; // Write each buffer individually on the socket. The retain() here is needed to preserve the fact that // ByteBuf are automatically released after a write. If the ByteBufPair ref count is increased and it // gets written multiple times, the individual buffers refcount should be reflected as well. try { ctx.write(b.getFirst().retainedDuplicate(), ctx.voidPromise()); ctx.write(b.getSecond().retainedDuplicate(), promise); } finally { ReferenceCountUtil.safeRelease(b); } } else { ctx.write(msg, promise); } } } @Sharable public static class CopyingEncoder extends ChannelOutboundHandlerAdapter { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof ByteBufPair) { ByteBufPair b = (ByteBufPair) msg; // Some handlers in the pipeline will modify the bytebufs passed in to them (i.e. SslHandler). // For these handlers, we need to pass a copy of the buffers as the source buffers may be cached // for multiple requests. try { ctx.write(b.getFirst().copy(), ctx.voidPromise()); ctx.write(b.getSecond().copy(), promise); } finally { ReferenceCountUtil.safeRelease(b); } } else { ctx.write(msg, promise); } } } }