View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Portions copyright 2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.audit.handlers.json;
18  
19  import java.io.OutputStream;
20  import java.nio.ByteBuffer;
21  
22  /**
23   * Wraps a {@link ByteBuffer} to expose the {@link OutputStream} interface, and grows the underlying buffer as
24   * data is added to it.
25   */
26  class ByteBufferOutputStream extends OutputStream {
27  
28      /**
29       * Buffer grows 30% when re-sized.
30       */
31      private static final double GROWTH_RATE = 1.33;
32  
33      private ByteBuffer buffer;
34  
35      /**
36       * Creates a {@code ByteBufferOutputStream} with the given {@link ByteBuffer}.
37       *
38       * @param buffer Initial buffer instance
39       */
40      public ByteBufferOutputStream(final ByteBuffer buffer) {
41          this.buffer = buffer;
42      }
43  
44      /**
45       * Gets the underlying buffer.
46       *
47       * @return Underlying buffer
48       */
49      public ByteBuffer byteBuffer() {
50          return buffer;
51      }
52  
53      /**
54       * Clears the buffer, but does not erase the underlying data, making it ready to be reused to accept new data.
55       */
56      public void clear() {
57          buffer.clear();
58      }
59  
60      /**
61       * Replaces the existing buffer with a new buffer having increased size.
62       *
63       * @param newCapacity New capacity
64       */
65      private void growTo(final int newCapacity) {
66          final ByteBuffer oldBuffer = buffer;
67          if (buffer.isDirect()) {
68              buffer = ByteBuffer.allocateDirect(newCapacity);
69          } else {
70              buffer = ByteBuffer.allocate(newCapacity);
71          }
72          // copy data
73          oldBuffer.flip();
74          buffer.put(oldBuffer);
75      }
76  
77      @Override
78      public void write(final int b) {
79          if (buffer.remaining() == 0) {
80              growTo((int) (buffer.capacity() * GROWTH_RATE));
81          }
82          buffer.put((byte) b);
83      }
84  
85      @Override
86      public void write(final byte[] bytes) {
87          if (buffer.remaining() < bytes.length) {
88              final int newSize = Math.max((int) (buffer.capacity() * GROWTH_RATE),
89                      buffer.position() + bytes.length);
90              growTo(newSize);
91          }
92          buffer.put(bytes);
93      }
94  
95      @Override
96      public void write(final byte[] bytes, final int off, final int len) {
97          if (len < 0) {
98              throw new IllegalArgumentException("len must be positive");
99          }
100         if (buffer.remaining() < len) {
101             final int newSize = Math.max((int) (buffer.capacity() * GROWTH_RATE), buffer.position() + len);
102             growTo(newSize);
103         }
104         buffer.put(bytes, off, len);
105     }
106 }