mirror of
https://github.com/yacy/yacy_search_server.git
synced 2024-09-19 00:01:41 +02:00
Removed more unsafe concurrent accesses to SimpleDateFormat instances.
SimpleDateFormat must not be used by concurrent threads without synchronization for parsing or formating dates as it is not thread-safe (internally holds a calendar instance that is not synchronized). Prefer now DateTimeFormatter when possible as it is thread-safe without concurrent access performance bottleneck (does not internally use synchronization locks).
This commit is contained in:
parent
5c6c61809a
commit
f895745e1c
|
@ -24,7 +24,8 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
|
@ -34,6 +35,7 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.yacy.cora.date.GenericFormatter;
|
||||
import net.yacy.cora.document.encoding.ASCII;
|
||||
import net.yacy.cora.document.encoding.UTF8;
|
||||
import net.yacy.cora.document.id.Punycode.PunycodeException;
|
||||
|
@ -350,11 +352,17 @@ public class CrawlResults {
|
|||
return prop;
|
||||
}
|
||||
|
||||
private static SimpleDateFormat dayFormatter = new SimpleDateFormat("yyyy/MM/dd", Locale.US);
|
||||
private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter
|
||||
.ofPattern("uuuu/MM/dd", Locale.US).withZone(ZoneId.systemDefault());
|
||||
|
||||
/**
|
||||
* @param date a date to render as a String
|
||||
* @return the date formatted using the DAY_FORMATTER pattern.
|
||||
*/
|
||||
private static String daydate(final Date date) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
return dayFormatter.format(date);
|
||||
return GenericFormatter.formatSafely(date.toInstant(), DAY_FORMATTER);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
import java.net.MalformedURLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
|
@ -12,6 +13,7 @@ import java.util.Set;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import net.yacy.cora.date.GenericFormatter;
|
||||
import net.yacy.cora.document.encoding.ASCII;
|
||||
import net.yacy.cora.document.id.DigestURL;
|
||||
import net.yacy.cora.protocol.Domains;
|
||||
|
@ -28,10 +30,18 @@ import net.yacy.server.serverSwitch;
|
|||
|
||||
public class IndexCreateQueues_p {
|
||||
|
||||
private static SimpleDateFormat dayFormatter = new SimpleDateFormat("yyyy/MM/dd", Locale.US);
|
||||
private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter
|
||||
.ofPattern("uuuu/MM/dd", Locale.US).withZone(ZoneId.systemDefault());
|
||||
|
||||
/**
|
||||
* @param date a date to render as a String
|
||||
* @return the date formatted using the DAY_FORMATTER pattern.
|
||||
*/
|
||||
private static String daydate(final Date date) {
|
||||
if (date == null) return "";
|
||||
return dayFormatter.format(date);
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
return GenericFormatter.formatSafely(date.toInstant(), DAY_FORMATTER);
|
||||
}
|
||||
|
||||
private static final int INVALID = 0;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
package net.yacy.cora.document.feed;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
@ -156,7 +157,7 @@ public class RSSMessage implements Hit, Comparable<RSSMessage>, Comparator<RSSMe
|
|||
if (title.length() > 0) this.map.put(Token.title.name(), title);
|
||||
if (description.length() > 0) this.map.put(Token.description.name(), description);
|
||||
if (link.length() > 0) this.map.put(Token.link.name(), link);
|
||||
this.map.put(Token.pubDate.name(), HeaderFramework.FORMAT_RFC1123.format(new Date()));
|
||||
this.map.put(Token.pubDate.name(), HeaderFramework.formatNowRFC1123());
|
||||
this.map.put(Token.guid.name(), artificialGuidPrefix + Integer.toHexString((title + description + link).hashCode()));
|
||||
}
|
||||
|
||||
|
@ -165,7 +166,7 @@ public class RSSMessage implements Hit, Comparable<RSSMessage>, Comparator<RSSMe
|
|||
if (title.length() > 0) this.map.put(Token.title.name(), title);
|
||||
if (description.length() > 0) this.map.put(Token.description.name(), description);
|
||||
this.map.put(Token.link.name(), link.toNormalform(true));
|
||||
this.map.put(Token.pubDate.name(), HeaderFramework.FORMAT_RFC1123.format(new Date()));
|
||||
this.map.put(Token.pubDate.name(), HeaderFramework.formatNowRFC1123());
|
||||
if (guid.length() > 0) {
|
||||
this.map.put(Token.guid.name(), guid);
|
||||
}
|
||||
|
@ -261,8 +262,8 @@ public class RSSMessage implements Hit, Comparable<RSSMessage>, Comparator<RSSMe
|
|||
if (!dateString.isEmpty()) { // skip parse exception on empty string
|
||||
Date date;
|
||||
try {
|
||||
date = HeaderFramework.FORMAT_RFC1123.parse(dateString);
|
||||
} catch (final ParseException e) {
|
||||
date = Date.from(ZonedDateTime.parse(dateString, HeaderFramework.RFC1123_FORMATTER).toInstant());
|
||||
} catch (final RuntimeException e) {
|
||||
try {
|
||||
date = GenericFormatter.SHORT_SECOND_FORMATTER.parse(dateString, 0).getTime();
|
||||
} catch (final ParseException e1) {
|
||||
|
@ -401,7 +402,7 @@ public class RSSMessage implements Hit, Comparable<RSSMessage>, Comparator<RSSMe
|
|||
|
||||
@Override
|
||||
public void setPubDate(final Date pubdate) {
|
||||
setValue(Token.pubDate, HeaderFramework.FORMAT_RFC1123.format(pubdate));
|
||||
setValue(Token.pubDate, HeaderFramework.formatNowRFC1123());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,10 @@ import java.io.FileOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
|
@ -230,10 +234,33 @@ public class HeaderFramework extends TreeMap<String, String> implements Map<Stri
|
|||
/** Date formatter/parser for standard compliant HTTP header dates (RFC 1123) */
|
||||
private static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss Z"; // with numeric time zone indicator as defined in RFC5322
|
||||
private static final String PATTERN_RFC1036 = "EEEE, dd-MMM-yy HH:mm:ss zzz";
|
||||
public static final SimpleDateFormat FORMAT_RFC1123 = new SimpleDateFormat(PATTERN_RFC1123, Locale.US);
|
||||
public static final SimpleDateFormat FORMAT_RFC1036 = new SimpleDateFormat(PATTERN_RFC1036, Locale.US);
|
||||
private static final SimpleDateFormat FORMAT_RFC1123 = new SimpleDateFormat(PATTERN_RFC1123, Locale.US);
|
||||
private static final TimeZone TZ_GMT = TimeZone.getTimeZone("GMT");
|
||||
private static final Calendar CAL_GMT = Calendar.getInstance(TZ_GMT, Locale.US);
|
||||
|
||||
/**
|
||||
* A thread-safe date formatter using the
|
||||
* {@link HeaderFramework#PATTERN_RFC1123} pattern with the US locale on the UTC
|
||||
* time zone.
|
||||
*/
|
||||
public static final DateTimeFormatter RFC1123_FORMATTER = DateTimeFormatter
|
||||
.ofPattern(PATTERN_RFC1123.replace("yyyy", "uuuu")).withLocale(Locale.US).withZone(ZoneOffset.UTC);
|
||||
|
||||
/**
|
||||
* @return a new SimpleDateFormat instance using the
|
||||
* {@link HeaderFramework#PATTERN_RFC1123} pattern with the US locale.
|
||||
*/
|
||||
public static SimpleDateFormat newRfc1123Format() {
|
||||
return new SimpleDateFormat(HeaderFramework.PATTERN_RFC1123, Locale.US);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a new SimpleDateFormat instance using the
|
||||
* {@link HeaderFramework#PATTERN_RFC1036} pattern with the US locale.
|
||||
*/
|
||||
public static SimpleDateFormat newRfc1036Format() {
|
||||
return new SimpleDateFormat(HeaderFramework.PATTERN_RFC1036, Locale.US);
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC 2616 requires that HTTP clients are able to parse all 3 different
|
||||
|
@ -241,9 +268,9 @@ public class HeaderFramework extends TreeMap<String, String> implements Map<Stri
|
|||
*/
|
||||
private static final SimpleDateFormat[] FORMATS_HTTP = new SimpleDateFormat[] {
|
||||
// RFC 1123/822 (Standard) "Mon, 12 Nov 2007 10:11:12 GMT"
|
||||
FORMAT_RFC1123,
|
||||
newRfc1123Format(),
|
||||
// RFC 1036/850 (old) "Monday, 12-Nov-07 10:11:12 GMT"
|
||||
FORMAT_RFC1036,
|
||||
newRfc1036Format(),
|
||||
// ANSI C asctime() "Mon Nov 12 10:11:12 2007"
|
||||
GenericFormatter.newAnsicFormat(),
|
||||
};
|
||||
|
@ -265,6 +292,34 @@ public class HeaderFramework extends TreeMap<String, String> implements Map<Stri
|
|||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param epochMilli
|
||||
* a time value as the number of milliseconds from Epoch
|
||||
* (1970-01-01T00:00:00Z)
|
||||
* @return the time formatted using the {@link HeaderFramework#PATTERN_RFC1123}
|
||||
* pattern.
|
||||
*/
|
||||
public static final String formatRFC1123(final long epochMilli) {
|
||||
try {
|
||||
/* Prefer first using the thread-safe DateTimeFormatter shared instance */
|
||||
return RFC1123_FORMATTER.format(Instant.ofEpochMilli(epochMilli));
|
||||
} catch (final DateTimeException e) {
|
||||
/*
|
||||
* This should not happen, but rather than failing we prefer here to use
|
||||
* formatting function using the synchronized SimpleDateFormat
|
||||
*/
|
||||
return formatRFC1123(new Date(epochMilli));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current time formatted using the
|
||||
* {@link HeaderFramework#PATTERN_RFC1123} pattern.
|
||||
*/
|
||||
public static final String formatNowRFC1123() {
|
||||
return formatRFC1123(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/** Initialization of static formats */
|
||||
static {
|
||||
|
@ -277,6 +332,9 @@ public class HeaderFramework extends TreeMap<String, String> implements Map<Stri
|
|||
format.setTimeZone(TZ_GMT);
|
||||
format.set2DigitYearStart(CAL_GMT.getTime());
|
||||
}
|
||||
|
||||
FORMAT_RFC1123.setTimeZone(TZ_GMT);
|
||||
FORMAT_RFC1123.set2DigitYearStart(CAL_GMT.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,29 @@
|
|||
// HeaderFrameworkTest.java
|
||||
// Copyright 2006-2018 by theli, f1ori, Michael Peter Christen; mc@yacy.net, reger; reger18@arcor.de, luccioman; https://github.com/luccioman
|
||||
//
|
||||
// This is a part of YaCy, a peer-to-peer based web search engine
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package net.yacy.cora.protocol;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -18,6 +41,22 @@ public class HeaderFrameworkTest extends TestCase {
|
|||
|
||||
// Print Result
|
||||
System.out.println("testParseHTTPDate: " + parsedDate.toString());
|
||||
|
||||
parsedDate = HeaderFramework.parseHTTPDate("Monday, 12-Nov-07 10:11:12 GMT");
|
||||
|
||||
// returned date must not be null
|
||||
assertNotNull(parsedDate);
|
||||
|
||||
// Print Result
|
||||
System.out.println("testParseHTTPDate: " + parsedDate.toString());
|
||||
|
||||
parsedDate = HeaderFramework.parseHTTPDate("Mon Nov 12 10:11:12 2007");
|
||||
|
||||
// returned date must not be null
|
||||
assertNotNull(parsedDate);
|
||||
|
||||
// Print Result
|
||||
System.out.println("testParseHTTPDate: " + parsedDate.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,4 +70,16 @@ public class HeaderFrameworkTest extends TestCase {
|
|||
assertEquals("utf-8", HeaderFramework.getCharacterEncoding("Text/HTML;Charset=\"utf-8\""));
|
||||
assertEquals("utf-8", HeaderFramework.getCharacterEncoding("text/html; charset=\"utf-8\""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit test for date formatting with RFC 1123 format
|
||||
*/
|
||||
@Test
|
||||
public void testFormatRfc1123() {
|
||||
assertEquals("", HeaderFramework.formatRFC1123(null));
|
||||
|
||||
final Instant time = Instant.parse("2018-06-29T13:04:55.00Z");
|
||||
assertEquals("Fri, 29 Jun 2018 13:04:55 +0000", HeaderFramework.formatRFC1123(time.toEpochMilli()));
|
||||
assertEquals("Fri, 29 Jun 2018 13:04:55 +0000", HeaderFramework.formatRFC1123(Date.from(time)));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user