Author: tomekr Date: Wed Oct 7 12:52:09 2015 New Revision: 1707283 URL: http://svn.apache.org/viewvc?rev=1707283&view=rev Log: SLING-4544 Performance: MessageFormat shouldn't be used for logging in SlingRequestProgressTracker
Added: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/FastMessageFormat.java sling/trunk/bundles/engine/src/test/java/org/apache/sling/engine/impl/request/FastMessageFormatTest.java Modified: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/SlingRequestProgressTracker.java Added: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/FastMessageFormat.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/FastMessageFormat.java?rev=1707283&view=auto ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/FastMessageFormat.java (added) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/FastMessageFormat.java Wed Oct 7 12:52:09 2015 @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.engine.impl.request; + +import java.text.DateFormat; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.util.Date; + +/** + * Fast MessageFormat implementation which is not thread-safe. It is based on the assumptions that + * <ul> + * <li>most formats do not contain format types and styles</li> + * <li>do not use escaping</li> + * <li>format elements are in order</li> + * </ul> + * + * If one of these assumptions fails, then it falls back to the original {@link MessageFormat}.<br> + * To increase the benefit of this implementation, every instance should be reused as often as possible. + */ +public class FastMessageFormat { + + // Reusable formats instances. Cannot be static because these classes are not thread safe. + private NumberFormat numberFormat; + private DateFormat dateFormat; + + private NumberFormat getNumberFormat() { + if (numberFormat == null) { + numberFormat = NumberFormat.getNumberInstance(); + } + return numberFormat; + } + + private DateFormat getDateFormat() { + if (dateFormat == null) { + dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); + } + return dateFormat; + } + + /** + * Use this method exactly like {@link MessageFormat#format(String, Object...)}. + * + * @see MessageFormat#format(String, Object...) + */ + public String format(String pattern, Object... arguments) { + if (arguments == null || arguments.length == 0) { + return pattern; + } else { + if (pattern.indexOf('\'') != -1) { + // Escaping is not supported, fall back + return MessageFormat.format(pattern, arguments); + } else { + StringBuilder message = new StringBuilder(); + int previousEnd = 0; + for (int i = 0; i < arguments.length; i++) { + String placeholder = '{' + String.valueOf(i); + int placeholderIndex = pattern.indexOf(placeholder); + // -1 or before previous placeholder || format element with type/style + if (placeholderIndex < previousEnd + || pattern.charAt(placeholderIndex + placeholder.length()) != '}') { + // Type, style and random order are not supported, fall back + return MessageFormat.format(pattern, arguments); + } else { + // Format argument if necessary + Object argument = arguments[i]; + if (argument instanceof Number) { + argument = getNumberFormat().format(argument); + } else if (argument instanceof Date) { + argument = getDateFormat().format(argument); + } + + // Append previous part of the string and formatted argument + message.append(pattern.substring(previousEnd, placeholderIndex)); + message.append(argument); + previousEnd = placeholderIndex + placeholder.length() + 1; + } + } + message.append(pattern.substring(previousEnd, pattern.length())); + return message.toString(); + } + } + } + +} Modified: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/SlingRequestProgressTracker.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/SlingRequestProgressTracker.java?rev=1707283&r1=1707282&r2=1707283&view=diff ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/SlingRequestProgressTracker.java (original) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/request/SlingRequestProgressTracker.java Wed Oct 7 12:52:09 2015 @@ -19,7 +19,6 @@ package org.apache.sling.engine.impl.request; import java.io.PrintWriter; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -129,6 +128,8 @@ public class SlingRequestProgressTracker */ private final Map<String, Long> namedTimerEntries = new HashMap<String, Long>(); + private final FastMessageFormat messageFormat = new FastMessageFormat(); + /** * Creates a new request progress tracker. */ @@ -210,7 +211,7 @@ public class SlingRequestProgressTracker /** Creates an entry with the given entry tag and message */ public void log(String format, Object... args) { - String message = MessageFormat.format(format, args); + String message = messageFormat.format(format, args); entries.add(new TrackingEntry(LOG_PREFIX + message)); } @@ -250,7 +251,7 @@ public class SlingRequestProgressTracker */ public void logTimer(String name, String format, Object... args) { if (namedTimerEntries.containsKey(name)) { - logTimerInternal(name, MessageFormat.format(format, args), namedTimerEntries.get(name)); + logTimerInternal(name, messageFormat.format(format, args), namedTimerEntries.get(name)); } } Added: sling/trunk/bundles/engine/src/test/java/org/apache/sling/engine/impl/request/FastMessageFormatTest.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/test/java/org/apache/sling/engine/impl/request/FastMessageFormatTest.java?rev=1707283&view=auto ============================================================================== --- sling/trunk/bundles/engine/src/test/java/org/apache/sling/engine/impl/request/FastMessageFormatTest.java (added) +++ sling/trunk/bundles/engine/src/test/java/org/apache/sling/engine/impl/request/FastMessageFormatTest.java Wed Oct 7 12:52:09 2015 @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.engine.impl.request; + +import org.junit.Test; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; + +import static org.junit.Assert.assertEquals; + +public class FastMessageFormatTest { + + @Test + public void testFormat() { + FormatTest[] tests = { + // Data types + new FormatTest("{0}", 1), + new FormatTest("{0}", Integer.MAX_VALUE), + new FormatTest("{0}", Long.MAX_VALUE), + new FormatTest("{0}", Math.PI), + new FormatTest("{0}", 123456.123456), + new FormatTest("{0}", Float.MAX_VALUE), + new FormatTest("{0}", new Date()), + new FormatTest("{0}", Calendar.getInstance()), + new FormatTest("{0}", true), + new FormatTest("{0}", false), + new FormatTest("{0}", new int[] {1,2,3}), + new FormatTest("{0}", (Object) new Integer[] {1,2,3}), + new FormatTest("{0}", Arrays.asList(1, 2, 3)), + new FormatTest("{0}", "text"), + + // Patterns + new FormatTest("{0}{0}", 1, 2), + new FormatTest("a{0}b{1}c", 1, 2), + new FormatTest("a{0}b{1}c{2}d", 1, 2, 3), + new FormatTest("a{0}bb{1}ccc{2}dddd", 10, 20, 30), + new FormatTest("c{1}b{0}a", 1, 2), + new FormatTest("c{1}b{0}a{1}c", 1, 2), + + // Type/style + new FormatTest("c{0,number,#.##}b{0}a{1}c", 1, 2), + new FormatTest("{0,number,#.##}", 1.2345), + + // Escaping + new FormatTest("'{0}'", 1) + }; + + FastMessageFormat fastMessageFormat = new FastMessageFormat(); + for (FormatTest test : tests) { + String expected = MessageFormat.format(test.pattern, test.args); + String actual = fastMessageFormat.format(test.pattern, test.args); + assertEquals(expected, actual); + } + } + + private static class FormatTest { + final String pattern; + final Object[] args; + public FormatTest(String pattern, Object... args) { + this.pattern = pattern; + this.args = args; + } + } + +}