001: /*
002: * Copyright 2004-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.compass.core.converter.basic;
018:
019: import java.text.Format;
020: import java.text.ParseException;
021: import java.util.Locale;
022: import org.compass.core.converter.ConversionException;
023:
024: /**
025: * Wrapper around <code>java.text.Format</code> that can be called by multiple
026: * threads concurrently.
027: * <p/>
028: * Format has a high overhead in creating and is not thread safe. To make best
029: * use of resources, the ThreadSafeFormat provides a dynamically sizing pool of
030: * instances, each of which will only be called by a single thread at a time.
031: * <p/>
032: * The pool has a maximum capacity, to limit overhead. If all instances in the
033: * pool are in use and another is required, it shall block until one becomes
034: * available.
035: *
036: * @author kimchy
037: */
038: public class ThreadSafeFormat {
039:
040: public static interface FormatterFactory {
041:
042: void configure(String format, Locale locale);
043:
044: Format create();
045: }
046:
047: private final int initialPoolSize;
048:
049: private final int maxPoolSize;
050:
051: private transient Format[] pool;
052:
053: private int nextAvailable = 0;
054:
055: private final Object mutex = new Object();
056:
057: private final FormatterFactory formatterFactory;
058:
059: public ThreadSafeFormat(int initialPoolSize, int maxPoolSize,
060: FormatterFactory formatterFactory) {
061: this .initialPoolSize = initialPoolSize;
062: this .maxPoolSize = maxPoolSize;
063: this .formatterFactory = formatterFactory;
064: }
065:
066: public String format(Object obj) {
067: Format format = fetchFromPool();
068: try {
069: return format.format(obj);
070: } finally {
071: putInPool(format);
072: }
073: }
074:
075: public Object parse(String date) throws ParseException {
076: Format format = fetchFromPool();
077: try {
078: return format.parseObject(date);
079: } finally {
080: putInPool(format);
081: }
082: }
083:
084: private Format fetchFromPool() {
085: Format result;
086: synchronized (mutex) {
087: if (pool == null) {
088: nextAvailable = -1;
089: pool = new Format[maxPoolSize];
090: for (int i = 0; i < initialPoolSize; i++) {
091: putInPool(formatterFactory.create());
092: }
093: }
094: while (nextAvailable < 0) {
095: try {
096: mutex.wait();
097: } catch (InterruptedException e) {
098: throw new ConversionException(
099: "Interrupted whilst waiting for a free item in the pool",
100: e);
101: }
102: }
103: result = pool[nextAvailable];
104: nextAvailable--;
105: }
106: if (result == null) {
107: result = formatterFactory.create();
108: putInPool(result);
109: }
110: return result;
111: }
112:
113: private void putInPool(Format format) {
114: synchronized (mutex) {
115: nextAvailable++;
116: pool[nextAvailable] = format;
117: mutex.notify();
118: }
119: }
120:
121: }
|