1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.tomcat.util.http; 19 20 import java.text.DateFormat; 21 import java.text.ParseException; 22 import java.text.SimpleDateFormat; 23 import java.util.Date; 24 import java.util.Locale; 25 import java.util.TimeZone; 26 import java.util.concurrent.ConcurrentHashMap; 27 28 /** 29 * Utility class to generate HTTP dates. 30 * 31 * @author Remy Maucherat 32 */ 33 public final class FastHttpDateFormat { 34 35 36 // -------------------------------------------------------------- Variables 37 38 39 protected static final int CACHE_SIZE = 40 Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000")); 41 42 43 /** 44 * HTTP date format. 45 */ 46 protected static final SimpleDateFormat format = 47 new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); 48 49 50 /** 51 * The set of SimpleDateFormat formats to use in getDateHeader(). 52 */ 53 protected static final SimpleDateFormat formats[] = { 54 new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), 55 new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), 56 new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) 57 }; 58 59 60 protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT"); 61 62 63 /** 64 * GMT timezone - all HTTP dates are on GMT 65 */ 66 static { 67 68 format.setTimeZone(gmtZone); 69 70 formats[0].setTimeZone(gmtZone); 71 formats[1].setTimeZone(gmtZone); 72 formats[2].setTimeZone(gmtZone); 73 74 } 75 76 77 /** 78 * Instant on which the currentDate object was generated. 79 */ 80 protected static long currentDateGenerated = 0L; 81 82 83 /** 84 * Current formatted date. 85 */ 86 protected static String currentDate = null; 87 88 89 /** 90 * Formatter cache. 91 */ 92 protected static final ConcurrentHashMap<Long, String> formatCache = 93 new ConcurrentHashMap<Long, String>(CACHE_SIZE); 94 95 96 /** 97 * Parser cache. 98 */ 99 protected static final ConcurrentHashMap<String, Long> parseCache = 100 new ConcurrentHashMap<String, Long>(CACHE_SIZE); 101 102 103 // --------------------------------------------------------- Public Methods 104 105 106 /** 107 * Get the current date in HTTP format. 108 */ 109 public static final String getCurrentDate() { 110 111 long now = System.currentTimeMillis(); 112 if ((now - currentDateGenerated) > 1000) { 113 synchronized (format) { 114 if ((now - currentDateGenerated) > 1000) { 115 currentDateGenerated = now; 116 currentDate = format.format(new Date(now)); 117 } 118 } 119 } 120 return currentDate; 121 122 } 123 124 125 /** 126 * Get the HTTP format of the specified date. 127 */ 128 public static final String formatDate 129 (long value, DateFormat threadLocalformat) { 130 131 Long longValue = new Long(value); 132 String cachedDate = formatCache.get(longValue); 133 if (cachedDate != null) 134 return cachedDate; 135 136 String newDate = null; 137 Date dateValue = new Date(value); 138 if (threadLocalformat != null) { 139 newDate = threadLocalformat.format(dateValue); 140 updateFormatCache(longValue, newDate); 141 } else { 142 synchronized (formatCache) { 143 synchronized (format) { 144 newDate = format.format(dateValue); 145 } 146 updateFormatCache(longValue, newDate); 147 } 148 } 149 return newDate; 150 151 } 152 153 154 /** 155 * Try to parse the given date as a HTTP date. 156 */ 157 public static final long parseDate(String value, 158 DateFormat[] threadLocalformats) { 159 160 Long cachedDate = parseCache.get(value); 161 if (cachedDate != null) 162 return cachedDate.longValue(); 163 164 Long date = null; 165 if (threadLocalformats != null) { 166 date = internalParseDate(value, threadLocalformats); 167 updateParseCache(value, date); 168 } else { 169 synchronized (parseCache) { 170 date = internalParseDate(value, formats); 171 updateParseCache(value, date); 172 } 173 } 174 if (date == null) { 175 return (-1L); 176 } else { 177 return date.longValue(); 178 } 179 180 } 181 182 183 /** 184 * Parse date with given formatters. 185 */ 186 private static final Long internalParseDate 187 (String value, DateFormat[] formats) { 188 Date date = null; 189 for (int i = 0; (date == null) && (i < formats.length); i++) { 190 try { 191 date = formats[i].parse(value); 192 } catch (ParseException e) { 193 ; 194 } 195 } 196 if (date == null) { 197 return null; 198 } 199 return new Long(date.getTime()); 200 } 201 202 203 /** 204 * Update cache. 205 */ 206 private static void updateFormatCache(Long key, String value) { 207 if (value == null) { 208 return; 209 } 210 if (formatCache.size() > CACHE_SIZE) { 211 formatCache.clear(); 212 } 213 formatCache.put(key, value); 214 } 215 216 217 /** 218 * Update cache. 219 */ 220 private static void updateParseCache(String key, Long value) { 221 if (value == null) { 222 return; 223 } 224 if (parseCache.size() > CACHE_SIZE) { 225 parseCache.clear(); 226 } 227 parseCache.put(key, value); 228 } 229 230 231 }