View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.openr66.protocol.localhandler.packet;
21  
22  import io.netty.buffer.ByteBuf;
23  import io.netty.buffer.ByteBufAllocator;
24  import io.netty.buffer.Unpooled;
25  import io.netty.util.IllegalReferenceCountException;
26  import org.waarp.common.utility.WaarpNettyUtil;
27  import org.waarp.openr66.protocol.exception.OpenR66ProtocolPacketException;
28  import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
29  import org.waarp.openr66.protocol.networkhandler.packet.NetworkPacket;
30  
31  import static org.waarp.openr66.protocol.networkhandler.packet.NetworkPacket.*;
32  
33  /**
34   * This class represents Abstract Packet with its header, middle and end parts.
35   * A Packet is composed of one
36   * Header part, one Middle part (data), and one End part. Header: length field
37   * (4 bytes) = Middle length field
38   * (4 bytes), End length field (4 bytes), type field (1 byte), ...<br>
39   * Middle: (Middle length field bytes)<br>
40   * End: (End length field bytes) = code status field (4 bytes), ...<br>
41   */
42  public abstract class AbstractLocalPacket {
43    protected static final byte[] EMPTY_ARRAY = {};
44    protected static final int LOCAL_HEADER_SIZE = 4 * 3 + 1;
45  
46    protected ByteBuf header;
47  
48    protected ByteBuf middle;
49  
50    protected ByteBuf end;
51  
52    protected ByteBuf global = null;
53  
54    protected AbstractLocalPacket() {
55      header = null;
56      middle = null;
57      end = null;
58    }
59  
60    /**
61     * @return True if all buffers fit in one piece
62     */
63    public abstract boolean hasGlobalBuffer();
64  
65    /**
66     * Prepare the 3 buffers Header, Middle and End
67     *
68     * @throws OpenR66ProtocolPacketException
69     */
70    public abstract void createAllBuffers(final LocalChannelReference lcr,
71                                          final int networkHeader)
72        throws OpenR66ProtocolPacketException;
73  
74    /**
75     * Prepare the Header buffer
76     *
77     * @throws OpenR66ProtocolPacketException
78     */
79    public void createHeader(final LocalChannelReference lcr)
80        throws OpenR66ProtocolPacketException {
81      throw new IllegalStateException("Should not be called");
82    }
83  
84    /**
85     * Prepare the Middle buffer
86     *
87     * @throws OpenR66ProtocolPacketException
88     */
89    public void createMiddle(final LocalChannelReference lcr)
90        throws OpenR66ProtocolPacketException {
91      throw new IllegalStateException("Should not be called");
92    }
93  
94    /**
95     * Prepare the End buffer
96     *
97     * @throws OpenR66ProtocolPacketException
98     */
99    public void createEnd(final LocalChannelReference lcr)
100       throws OpenR66ProtocolPacketException {
101     throw new IllegalStateException("Should not be called");
102   }
103 
104   /**
105    * @return the type of Packet
106    */
107   public abstract byte getType();
108 
109   @Override
110   public abstract String toString();
111 
112   /**
113    * @param lcr the LocalChannelReference in use
114    *
115    * @return the ByteBuf as LocalPacket
116    *
117    * @throws OpenR66ProtocolPacketException
118    */
119   public final ByteBuf getLocalPacket(final LocalChannelReference lcr)
120       throws OpenR66ProtocolPacketException {
121     return getLocalPacketForNetworkPacket(lcr, null);
122   }
123 
124   /**
125    * @param lcr the LocalChannelReference in use
126    *
127    * @return the ByteBuf as LocalPacket
128    *
129    * @throws OpenR66ProtocolPacketException
130    */
131   public final synchronized ByteBuf getLocalPacketForNetworkPacket(
132       final LocalChannelReference lcr, final NetworkPacket packet)
133       throws OpenR66ProtocolPacketException {
134     try {
135       final ByteBuf buf;
136       final int globalHeader;
137       if (packet != null) {
138         globalHeader = NETWORK_HEADER_SIZE;
139       } else {
140         globalHeader = 0;
141       }
142       if (hasGlobalBuffer()) {
143         if (global == null) {
144           createAllBuffers(lcr, globalHeader);
145         } else {
146           global.readerIndex(0);
147           global.writerIndex(0);
148         }
149         buf = global;
150       } else {
151         // 3 header lengths+type
152         buf =
153             ByteBufAllocator.DEFAULT.ioBuffer(globalHeader + LOCAL_HEADER_SIZE,
154                                               globalHeader + LOCAL_HEADER_SIZE);
155         if (header == null) {
156           createHeader(lcr);
157         }
158         if (middle == null) {
159           createMiddle(lcr);
160         }
161         if (end == null) {
162           createEnd(lcr);
163         }
164       }
165       if (packet != null) {
166         final int capacity =
167             LOCAL_HEADER_SIZE + (header != null? header.capacity() : 0) +
168             (middle != null? middle.capacity() : 0) +
169             (end != null? end.capacity() : 0);
170         packet.writeNetworkHeader(buf, capacity);
171       }
172       return getByteBuf(buf);
173     } catch (final IllegalReferenceCountException e) {
174       throw new OpenR66ProtocolPacketException(e);
175     }
176   }
177 
178   private ByteBuf getByteBuf(final ByteBuf buf) {
179     final ByteBuf newHeader = header != null? header : Unpooled.EMPTY_BUFFER;
180     final int headerLength = LOCAL_HEADER_SIZE - 4 + newHeader.readableBytes();
181     final ByteBuf newMiddle = middle != null? middle : Unpooled.EMPTY_BUFFER;
182     final int middleLength = newMiddle.readableBytes();
183     final ByteBuf newEnd = end != null? end : Unpooled.EMPTY_BUFFER;
184     final int endLength = newEnd.readableBytes();
185     buf.writeInt(headerLength);
186     buf.writeInt(middleLength);
187     buf.writeInt(endLength);
188     buf.writeByte(getType());
189     if (hasGlobalBuffer()) {
190       buf.writerIndex(buf.capacity());
191       return buf;
192     }
193     return ByteBufAllocator.DEFAULT.compositeDirectBuffer(4)
194                                    .addComponents(buf, newHeader, newMiddle,
195                                                   newEnd);
196   }
197 
198   public synchronized void clear() {
199     if (WaarpNettyUtil.release(global)) {
200       global = null;
201     }
202     if (hasGlobalBuffer()) {
203       return;
204     }
205     if (WaarpNettyUtil.release(header)) {
206       header = null;
207     }
208     if (WaarpNettyUtil.release(middle)) {
209       middle = null;
210     }
211     if (WaarpNettyUtil.release(end)) {
212       end = null;
213     }
214   }
215 
216   public final synchronized void retain() {
217     WaarpNettyUtil.retain(global);
218     if (hasGlobalBuffer()) {
219       return;
220     }
221     WaarpNettyUtil.retain(header);
222     WaarpNettyUtil.retain(middle);
223     WaarpNettyUtil.retain(end);
224   }
225 }