1 | /* |
2 | * @(#) $Id: SSLByteBufferPool.java 264677 2005-08-30 02:44:35Z trustin $ |
3 | * |
4 | * Copyright 2004 The Apache Software Foundation |
5 | * |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. |
8 | * You may obtain a copy of the License at |
9 | * |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * |
12 | * Unless required by applicable law or agreed to in writing, software |
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * See the License for the specific language governing permissions and |
16 | * limitations under the License. |
17 | * |
18 | */ |
19 | package org.apache.mina.io.filter; |
20 | |
21 | import java.nio.ByteBuffer; |
22 | |
23 | import javax.net.ssl.SSLEngine; |
24 | |
25 | import org.apache.mina.util.Stack; |
26 | |
27 | /** |
28 | * Simple ByteBuffer pool used by SSLHandler. |
29 | * ByteBuffers are by default allocated as direct byte buffers. To use non-direct |
30 | * ByteBuffers, set system property mina.sslfilter.directbuffer to false. |
31 | * |
32 | * @author Jan Andersson (janne@minq.se) |
33 | * @version $Rev: 264677 $, $Date: 2005-08-30 11:44:35 +0900 $ |
34 | */ |
35 | class SSLByteBufferPool |
36 | { |
37 | private static final int PACKET_BUFFER_INDEX = 0; |
38 | |
39 | private static final int APPLICATION_BUFFER_INDEX = 1; |
40 | |
41 | private static boolean initiated = false; |
42 | |
43 | private static final String DIRECT_MEMORY_PROP = "mina.sslfilter.directbuffer"; |
44 | |
45 | private static boolean useDirectAllocatedBuffers = true; |
46 | |
47 | private static int packetBufferSize; |
48 | |
49 | private static int appBufferSize; |
50 | |
51 | private static int[] bufferStackSizes; |
52 | |
53 | private static final Stack[] bufferStacks = new Stack[] { new Stack(), |
54 | new Stack(), }; |
55 | |
56 | /** |
57 | * Initiate buffer pool and buffer sizes from SSLEngine session. |
58 | * |
59 | * @param sslEngine SSLEngine |
60 | */ |
61 | static synchronized void initiate( SSLEngine sslEngine ) |
62 | { |
63 | if( !initiated ) |
64 | { |
65 | // Use direct allocated memory or not? |
66 | String prop = System.getProperty( DIRECT_MEMORY_PROP ); |
67 | if( prop != null ) |
68 | { |
69 | useDirectAllocatedBuffers = Boolean |
70 | .getBoolean( DIRECT_MEMORY_PROP ); |
71 | } |
72 | |
73 | // init buffer sizes from SSLEngine |
74 | packetBufferSize = sslEngine.getSession().getPacketBufferSize(); |
75 | |
76 | // application buffer size has been doubled because SSLEngine |
77 | // returns BUFFER_OVERFLOW even if there is enough room for the buffer. |
78 | // So for now we use a size double the packet size as a workaround. |
79 | appBufferSize = packetBufferSize * 2; |
80 | initiateBufferStacks(); |
81 | initiated = true; |
82 | } |
83 | } |
84 | |
85 | /** |
86 | * Get bytebuffer with size the size of the largest SSL/TLS packet that may occur |
87 | * (as defined by SSLSession). |
88 | */ |
89 | static ByteBuffer getPacketBuffer() |
90 | { |
91 | if( !initiated ) |
92 | { |
93 | throw new IllegalStateException( "Not initialized" ); |
94 | } |
95 | return allocate( PACKET_BUFFER_INDEX ); |
96 | } |
97 | |
98 | /** |
99 | * Get ByteBuffer with the size of the largest application buffer that may occur |
100 | * (as defined by SSLSession). |
101 | */ |
102 | static ByteBuffer getApplicationBuffer() |
103 | { |
104 | if( !initiated ) |
105 | { |
106 | throw new IllegalStateException( "Not initialized" ); |
107 | } |
108 | return allocate( APPLICATION_BUFFER_INDEX ); |
109 | } |
110 | |
111 | /** |
112 | * Allocate or get the buffer which is capable of the specified size. |
113 | */ |
114 | private static ByteBuffer allocate( int idx ) |
115 | { |
116 | Stack stack = bufferStacks[ idx ]; |
117 | |
118 | ByteBuffer buf; |
119 | synchronized( stack ) |
120 | { |
121 | buf = ( ByteBuffer ) stack.pop(); |
122 | if( buf == null ) |
123 | { |
124 | buf = createBuffer( bufferStackSizes[ idx ] ); |
125 | } |
126 | } |
127 | |
128 | buf.clear(); |
129 | return buf; |
130 | } |
131 | |
132 | /** |
133 | * Releases the specified buffer to buffer pool. |
134 | */ |
135 | public static void release( ByteBuffer buf ) |
136 | { |
137 | int stackIndex =getBufferStackIndex( buf.capacity() ); |
138 | if ( stackIndex >= PACKET_BUFFER_INDEX ) { |
139 | Stack stack = bufferStacks[getBufferStackIndex( buf.capacity() )]; |
140 | synchronized ( stack ) { |
141 | stack.push( buf ); |
142 | } |
143 | } |
144 | } |
145 | |
146 | /** |
147 | * Expand size of provided buffer |
148 | * @param buf buffer to be expande |
149 | * @param newCapacity new capacity |
150 | */ |
151 | public static ByteBuffer expandBuffer( ByteBuffer buf, int newCapacity ) |
152 | { |
153 | ByteBuffer newBuf = createBuffer( newCapacity ); |
154 | buf.flip(); |
155 | newBuf.put( buf ); |
156 | release(buf); |
157 | return newBuf; |
158 | } |
159 | |
160 | private static void initiateBufferStacks() |
161 | { |
162 | bufferStackSizes = new int[ 2 ]; |
163 | bufferStackSizes[ PACKET_BUFFER_INDEX ] = packetBufferSize; |
164 | bufferStackSizes[ APPLICATION_BUFFER_INDEX ] = appBufferSize; |
165 | } |
166 | |
167 | private static int getBufferStackIndex( int size ) |
168 | { |
169 | if( size == packetBufferSize ) |
170 | return PACKET_BUFFER_INDEX; |
171 | if( size == appBufferSize ) |
172 | return APPLICATION_BUFFER_INDEX; |
173 | return -1; // not reused |
174 | } |
175 | |
176 | private static ByteBuffer createBuffer( int capacity ) |
177 | { |
178 | if( useDirectAllocatedBuffers ) |
179 | { |
180 | try |
181 | { |
182 | return ByteBuffer.allocateDirect( capacity ); |
183 | } |
184 | catch( OutOfMemoryError e ) |
185 | { |
186 | useDirectAllocatedBuffers = false; |
187 | System.err |
188 | .println( "OutOfMemoryError: No more direct buffers available; trying heap buffer instead" ); |
189 | } |
190 | } |
191 | return ByteBuffer.allocate( capacity ); |
192 | } |
193 | |
194 | } |