1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.layout;
18
19 import java.nio.ByteBuffer;
20 import java.nio.CharBuffer;
21 import java.nio.charset.Charset;
22 import java.nio.charset.CharsetEncoder;
23 import java.nio.charset.CodingErrorAction;
24 import java.util.Objects;
25
26 import org.apache.logging.log4j.core.util.Constants;
27 import org.apache.logging.log4j.status.StatusLogger;
28
29
30
31
32 public class StringBuilderEncoder implements Encoder<StringBuilder> {
33
34 private static final int DEFAULT_BYTE_BUFFER_SIZE = 8 * 1024;
35
36
37
38
39
40
41
42
43
44
45
46 private final ThreadLocal<Object[]> threadLocal = new ThreadLocal<>();
47 private final Charset charset;
48 private final int charBufferSize;
49 private final int byteBufferSize;
50
51 public StringBuilderEncoder(final Charset charset) {
52 this(charset, Constants.ENCODER_CHAR_BUFFER_SIZE, DEFAULT_BYTE_BUFFER_SIZE);
53 }
54
55 public StringBuilderEncoder(final Charset charset, final int charBufferSize, final int byteBufferSize) {
56 this.charBufferSize = charBufferSize;
57 this.byteBufferSize = byteBufferSize;
58 this.charset = Objects.requireNonNull(charset, "charset");
59 }
60
61 @Override
62 public void encode(final StringBuilder source, final ByteBufferDestination destination) {
63 try {
64 final Object[] threadLocalState = getThreadLocalState();
65 final CharsetEncoder charsetEncoder = (CharsetEncoder) threadLocalState[0];
66 final CharBuffer charBuffer = (CharBuffer) threadLocalState[1];
67 final ByteBuffer byteBuffer = (ByteBuffer) threadLocalState[2];
68 TextEncoderHelper.encodeText(charsetEncoder, charBuffer, byteBuffer, source, destination);
69 } catch (final Exception ex) {
70 logEncodeTextException(ex, source, destination);
71 TextEncoderHelper.encodeTextFallBack(charset, source, destination);
72 }
73 }
74
75 private Object[] getThreadLocalState() {
76 Object[] threadLocalState = threadLocal.get();
77 if (threadLocalState == null) {
78 threadLocalState = new Object[] {
79 charset.newEncoder().onMalformedInput(CodingErrorAction.REPLACE)
80 .onUnmappableCharacter(CodingErrorAction.REPLACE),
81 CharBuffer.allocate(charBufferSize),
82 ByteBuffer.allocate(byteBufferSize)
83 };
84 threadLocal.set(threadLocalState);
85 } else {
86 ((CharsetEncoder) threadLocalState[0]).reset();
87 ((CharBuffer) threadLocalState[1]).clear();
88 ((ByteBuffer) threadLocalState[2]).clear();
89 }
90 return threadLocalState;
91 }
92
93 private void logEncodeTextException(final Exception ex, final StringBuilder text,
94 final ByteBufferDestination destination) {
95 StatusLogger.getLogger().error("Recovering from StringBuilderEncoder.encode('{}') error: {}", text, ex, ex);
96 }
97 }