1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. 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,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.myfaces.shared.util;
20
21 import java.io.IOException;
22 import java.io.Writer;
23 import java.lang.reflect.Field;
24
25 /**
26 * Provides optimized access to java.lang.String internals
27 *
28 * - Optimized way of creating java.lang.String by reusing a char[] buffer
29 * - Optimized way of writing String to java.io.Writer
30 *
31 * java.lang.String creation reusing a char[] buffer requires Java 1.5+
32 *
33 * System property "oam.stringchararrayaccessor.enabled" enables this hack.
34 * -Doam.stringchararrayaccessor.enabled=true
35 *
36 * Read JSR-133, "9.1.1 Post-Construction Modification of Final Fields"
37 * http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf
38 *
39 * @author Lari Hotari, Sagire Software Oy
40 * @see org.codehaus.groovy.grails.web.util.StreamCharBuffer
41 * file licensed under ASL v2.0
42 * Copyright 2009 the original author or authors.
43 */
44 public class StringCharArrayAccessor
45 {
46
47 //static volatile boolean enabled = !Boolean
48 // .getBoolean("oam.stringchararrayaccessor.disabled");
49 // In Google Application Engine this hack is not valid. We should
50 // set this one as default disabled.
51 static volatile boolean enabled = Boolean
52 .getBoolean("oam.stringchararrayaccessor.enabled");
53
54 static Field valueField;
55 static Field countField;
56 static Field offsetField;
57
58 static
59 {
60 if (enabled)
61 {
62 try
63 {
64 valueField = String.class.getDeclaredField("value");
65 valueField.setAccessible(true);
66
67 countField = String.class.getDeclaredField("count");
68 countField.setAccessible(true);
69
70 offsetField = String.class.getDeclaredField("offset");
71 offsetField.setAccessible(true);
72 }
73 catch (Exception e)
74 {
75 enabled = false;
76 System.err
77 .println("Unable to use direct char[] access of java.lang.String");
78 e.printStackTrace();
79 }
80 }
81 }
82
83 /**
84 * Writes a portion of a string to a target java.io.Writer with direct access to the char[] of the java.lang.String
85 *
86 * @param writer
87 * target java.io.Writer for output
88 *
89 * @param str
90 * A String
91 *
92 * @throws IOException
93 * If an I/O error occurs
94 */
95 static public void writeStringAsCharArray(Writer writer, String str)
96 throws IOException
97 {
98 writeStringAsCharArray(writer, str, 0, str.length());
99 }
100
101 /**
102 * Writes a portion of a string to a target java.io.Writer with direct access to the char[] of the java.lang.String
103 *
104 * @param writer
105 * target java.io.Writer for output
106 *
107 * @param str
108 * A String
109 *
110 * @param off
111 * Offset from which to start writing characters
112 *
113 * @param len
114 * Number of characters to write
115 *
116 * @throws IOException
117 * If an I/O error occurs
118 */
119 static public void writeStringAsCharArray(Writer writer, String str,
120 int off, int len) throws IOException
121 {
122 if (!enabled)
123 {
124 writeStringFallback(writer, str, off, len);
125 return;
126 }
127
128 char[] value;
129 int internalOffset;
130 try
131 {
132 value = (char[]) valueField.get(str);
133 internalOffset = offsetField.getInt(str);
134 }
135 catch (Exception e)
136 {
137 handleError(e);
138 writeStringFallback(writer, str, off, len);
139 return;
140 }
141 writer.write(value, internalOffset + off, len);
142 }
143
144 private static void writeStringFallback(Writer writer, String str, int off,
145 int len) throws IOException
146 {
147 writer.write(str, off, len);
148 }
149
150 static char[] getValue(String str)
151 {
152 if (!enabled)
153 {
154 return getValueFallback(str);
155 }
156
157 char[] value = null;
158 int internalOffset = 0;
159 try
160 {
161 value = (char[]) valueField.get(str);
162 internalOffset = offsetField.getInt(str);
163 }
164 catch (Exception e)
165 {
166 handleError(e);
167 }
168 if (value != null && internalOffset == 0)
169 {
170 return value;
171 }
172
173 return getValueFallback(str);
174 }
175
176 static char[] getValueFallback(String str)
177 {
178 return str.toCharArray();
179 }
180
181 /**
182 * creates a new java.lang.String by setting the char array directly to the String instance with reflection.
183 *
184 * @param charBuf
185 * char array to be used as java.lang.String content, don't modify it after passing it.
186 * @return new java.lang.String
187 */
188 public static String createString(char[] charBuf)
189 {
190 if (!enabled)
191 {
192 return createStringFallback(charBuf);
193 }
194
195 String str = new String();
196 try
197 {
198 // try to prevent possible final field setting execution reordering in JIT
199 // (JSR-133/JMM, "9.1.1 Post-Construction Modification of Final Fields")
200 // it was a bit unclear for me if this could ever happen in a single thread
201 synchronized (str)
202 {
203 valueField.set(str, charBuf);
204 countField.set(str, charBuf.length);
205 }
206 synchronized (str)
207 {
208 // safety check, just to be sure that setting the final fields went ok
209 if (str.length() != charBuf.length)
210 {
211 throw new IllegalStateException(
212 "Fast java.lang.String construction failed.");
213 }
214 }
215 }
216 catch (Exception e)
217 {
218 handleError(e);
219 str = createStringFallback(charBuf);
220 }
221 return str;
222 }
223
224 private static String createStringFallback(char[] charBuf)
225 {
226 return new String(charBuf);
227 }
228
229 private static synchronized void handleError(Exception e)
230 {
231 enabled = false;
232 System.err
233 .println("Unable to use direct char[] access of java.lang.String. Disabling this method.");
234 valueField = null;
235 countField = null;
236 offsetField = null;
237 e.printStackTrace();
238 }
239
240 static public boolean isEnabled()
241 {
242 return enabled;
243 }
244 }