View Javadoc

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 }