21
21
import java .io .IOException ;
22
22
import java .nio .charset .StandardCharsets ;
23
23
import java .util .Collections ;
24
+ import java .util .LinkedHashMap ;
24
25
import java .util .List ;
25
26
import java .util .Map ;
26
27
import java .util .Map .Entry ;
28
+ import java .util .concurrent .ConcurrentHashMap ;
27
29
28
30
import org .apache .commons .logging .Log ;
29
31
import org .apache .commons .logging .LogFactory ;
@@ -50,6 +52,27 @@ public class StompEncoder {
50
52
51
53
private static final Log logger = LogFactory .getLog (StompEncoder .class );
52
54
55
+ private static final int HEADER_KEY_CACHE_LIMIT = 32 ;
56
+
57
+
58
+ private final Map <String , byte []> headerKeyAccessCache =
59
+ new ConcurrentHashMap <>(HEADER_KEY_CACHE_LIMIT );
60
+
61
+ @ SuppressWarnings ("serial" )
62
+ private final Map <String , byte []> headerKeyUpdateCache =
63
+ new LinkedHashMap <String , byte []>(HEADER_KEY_CACHE_LIMIT , 0.75f , true ) {
64
+ @ Override
65
+ protected boolean removeEldestEntry (Map .Entry <String , byte []> eldest ) {
66
+ if (size () > HEADER_KEY_CACHE_LIMIT ) {
67
+ headerKeyAccessCache .remove (eldest .getKey ());
68
+ return true ;
69
+ }
70
+ else {
71
+ return false ;
72
+ }
73
+ }
74
+ };
75
+
53
76
54
77
/**
55
78
* Encodes the given STOMP {@code message} into a {@code byte[]}
@@ -130,11 +153,11 @@ private void writeHeaders(StompCommand command, Map<String, Object> headers, byt
130
153
values = Collections .singletonList (StompHeaderAccessor .getPasscode (headers ));
131
154
}
132
155
133
- byte [] encodedKey = encodeHeaderString (entry .getKey (), shouldEscape );
156
+ byte [] encodedKey = encodeHeaderKey (entry .getKey (), shouldEscape );
134
157
for (String value : values ) {
135
158
output .write (encodedKey );
136
159
output .write (COLON );
137
- output .write (encodeHeaderString (value , shouldEscape ));
160
+ output .write (encodeHeaderValue (value , shouldEscape ));
138
161
output .write (LF );
139
162
}
140
163
}
@@ -147,7 +170,23 @@ private void writeHeaders(StompCommand command, Map<String, Object> headers, byt
147
170
}
148
171
}
149
172
150
- private byte [] encodeHeaderString (String input , boolean escape ) {
173
+ private byte [] encodeHeaderKey (String input , boolean escape ) {
174
+ String inputToUse = (escape ? escape (input ) : input );
175
+ if (this .headerKeyAccessCache .containsKey (inputToUse )) {
176
+ return this .headerKeyAccessCache .get (inputToUse );
177
+ }
178
+ synchronized (this .headerKeyUpdateCache ) {
179
+ byte [] bytes = this .headerKeyUpdateCache .get (inputToUse );
180
+ if (bytes == null ) {
181
+ bytes = inputToUse .getBytes (StandardCharsets .UTF_8 );
182
+ this .headerKeyAccessCache .put (inputToUse , bytes );
183
+ this .headerKeyUpdateCache .put (inputToUse , bytes );
184
+ }
185
+ return bytes ;
186
+ }
187
+ }
188
+
189
+ private byte [] encodeHeaderValue (String input , boolean escape ) {
151
190
String inputToUse = (escape ? escape (input ) : input );
152
191
return inputToUse .getBytes (StandardCharsets .UTF_8 );
153
192
}
@@ -157,26 +196,38 @@ private byte[] encodeHeaderString(String input, boolean escape) {
157
196
* <a href="http://stomp.github.io/stomp-specification-1.2.html#Value_Encoding">"Value Encoding"</a>.
158
197
*/
159
198
private String escape (String inString ) {
160
- StringBuilder sb = new StringBuilder ( inString . length ()) ;
199
+ StringBuilder sb = null ;
161
200
for (int i = 0 ; i < inString .length (); i ++) {
162
201
char c = inString .charAt (i );
163
202
if (c == '\\' ) {
203
+ sb = getStringBuilder (sb , inString , i );
164
204
sb .append ("\\ \\ " );
165
205
}
166
206
else if (c == ':' ) {
207
+ sb = getStringBuilder (sb , inString , i );
167
208
sb .append ("\\ c" );
168
209
}
169
210
else if (c == '\n' ) {
170
- sb .append ("\\ n" );
211
+ sb = getStringBuilder (sb , inString , i );
212
+ sb .append ("\\ n" );
171
213
}
172
214
else if (c == '\r' ) {
215
+ sb = getStringBuilder (sb , inString , i );
173
216
sb .append ("\\ r" );
174
217
}
175
- else {
218
+ else if ( sb != null ) {
176
219
sb .append (c );
177
220
}
178
221
}
179
- return sb .toString ();
222
+ return (sb != null ? sb .toString () : inString );
223
+ }
224
+
225
+ private StringBuilder getStringBuilder (StringBuilder sb , String inString , int i ) {
226
+ if (sb == null ) {
227
+ sb = new StringBuilder (inString .length ());
228
+ sb .append (inString .substring (0 , i ));
229
+ }
230
+ return sb ;
180
231
}
181
232
182
233
private void writeBody (byte [] payload , DataOutputStream output ) throws IOException {
0 commit comments