001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.tomcat.util.http;
019:
020: import java.text.DateFormat;
021: import java.text.ParseException;
022: import java.text.SimpleDateFormat;
023: import java.util.Date;
024: import java.util.Locale;
025: import java.util.TimeZone;
026: import java.util.concurrent.ConcurrentHashMap;
027:
028: /**
029: * Utility class to generate HTTP dates.
030: *
031: * @author Remy Maucherat
032: */
033: public final class FastHttpDateFormat {
034:
035: // -------------------------------------------------------------- Variables
036:
037: protected static final int CACHE_SIZE = Integer
038: .parseInt(System
039: .getProperty(
040: "org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE",
041: "1000"));
042:
043: /**
044: * HTTP date format.
045: */
046: protected static final SimpleDateFormat format = new SimpleDateFormat(
047: "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
048:
049: /**
050: * The set of SimpleDateFormat formats to use in getDateHeader().
051: */
052: protected static final SimpleDateFormat formats[] = {
053: new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz",
054: Locale.US),
055: new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz",
056: Locale.US),
057: new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) };
058:
059: protected final static TimeZone gmtZone = TimeZone
060: .getTimeZone("GMT");
061:
062: /**
063: * GMT timezone - all HTTP dates are on GMT
064: */
065: static {
066:
067: format.setTimeZone(gmtZone);
068:
069: formats[0].setTimeZone(gmtZone);
070: formats[1].setTimeZone(gmtZone);
071: formats[2].setTimeZone(gmtZone);
072:
073: }
074:
075: /**
076: * Instant on which the currentDate object was generated.
077: */
078: protected static long currentDateGenerated = 0L;
079:
080: /**
081: * Current formatted date.
082: */
083: protected static String currentDate = null;
084:
085: /**
086: * Formatter cache.
087: */
088: protected static final ConcurrentHashMap<Long, String> formatCache = new ConcurrentHashMap<Long, String>(
089: CACHE_SIZE);
090:
091: /**
092: * Parser cache.
093: */
094: protected static final ConcurrentHashMap<String, Long> parseCache = new ConcurrentHashMap<String, Long>(
095: CACHE_SIZE);
096:
097: // --------------------------------------------------------- Public Methods
098:
099: /**
100: * Get the current date in HTTP format.
101: */
102: public static final String getCurrentDate() {
103:
104: long now = System.currentTimeMillis();
105: if ((now - currentDateGenerated) > 1000) {
106: synchronized (format) {
107: if ((now - currentDateGenerated) > 1000) {
108: currentDateGenerated = now;
109: currentDate = format.format(new Date(now));
110: }
111: }
112: }
113: return currentDate;
114:
115: }
116:
117: /**
118: * Get the HTTP format of the specified date.
119: */
120: public static final String formatDate(long value,
121: DateFormat threadLocalformat) {
122:
123: Long longValue = new Long(value);
124: String cachedDate = formatCache.get(longValue);
125: if (cachedDate != null)
126: return cachedDate;
127:
128: String newDate = null;
129: Date dateValue = new Date(value);
130: if (threadLocalformat != null) {
131: newDate = threadLocalformat.format(dateValue);
132: updateFormatCache(longValue, newDate);
133: } else {
134: synchronized (formatCache) {
135: synchronized (format) {
136: newDate = format.format(dateValue);
137: }
138: updateFormatCache(longValue, newDate);
139: }
140: }
141: return newDate;
142:
143: }
144:
145: /**
146: * Try to parse the given date as a HTTP date.
147: */
148: public static final long parseDate(String value,
149: DateFormat[] threadLocalformats) {
150:
151: Long cachedDate = parseCache.get(value);
152: if (cachedDate != null)
153: return cachedDate.longValue();
154:
155: Long date = null;
156: if (threadLocalformats != null) {
157: date = internalParseDate(value, threadLocalformats);
158: updateParseCache(value, date);
159: } else {
160: synchronized (parseCache) {
161: date = internalParseDate(value, formats);
162: updateParseCache(value, date);
163: }
164: }
165: if (date == null) {
166: return (-1L);
167: } else {
168: return date.longValue();
169: }
170:
171: }
172:
173: /**
174: * Parse date with given formatters.
175: */
176: private static final Long internalParseDate(String value,
177: DateFormat[] formats) {
178: Date date = null;
179: for (int i = 0; (date == null) && (i < formats.length); i++) {
180: try {
181: date = formats[i].parse(value);
182: } catch (ParseException e) {
183: ;
184: }
185: }
186: if (date == null) {
187: return null;
188: }
189: return new Long(date.getTime());
190: }
191:
192: /**
193: * Update cache.
194: */
195: private static void updateFormatCache(Long key, String value) {
196: if (value == null) {
197: return;
198: }
199: if (formatCache.size() > CACHE_SIZE) {
200: formatCache.clear();
201: }
202: formatCache.put(key, value);
203: }
204:
205: /**
206: * Update cache.
207: */
208: private static void updateParseCache(String key, Long value) {
209: if (value == null) {
210: return;
211: }
212: if (parseCache.size() > CACHE_SIZE) {
213: parseCache.clear();
214: }
215: parseCache.put(key, value);
216: }
217:
218: }
|