Use new date/time API

pull/30/head
Richard Körber 2017-02-11 11:56:26 +01:00
parent 0ed0a9219f
commit c1b677f310
23 changed files with 159 additions and 169 deletions

View File

@ -17,11 +17,11 @@ import static org.shredzone.acme4j.util.AcmeUtils.parseTimestamp;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Challenge;
@ -43,7 +43,7 @@ public class Authorization extends AcmeResource {
private String domain; private String domain;
private Status status; private Status status;
private Date expires; private Instant expires;
private List<Challenge> challenges; private List<Challenge> challenges;
private List<List<Challenge>> combinations; private List<List<Challenge>> combinations;
private boolean loaded = false; private boolean loaded = false;
@ -86,7 +86,7 @@ public class Authorization extends AcmeResource {
/** /**
* Gets the expiry date of the authorization, if set by the server. * Gets the expiry date of the authorization, if set by the server.
*/ */
public Date getExpires() { public Instant getExpires() {
load(); load();
return expires; return expires;
} }

View File

@ -19,10 +19,10 @@ import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -234,7 +234,7 @@ public class Registration extends AcmeResource {
* for default. May be ignored by the server. * for default. May be ignored by the server.
* @return The {@link Certificate} * @return The {@link Certificate}
*/ */
public Certificate requestCertificate(byte[] csr, Date notBefore, Date notAfter) public Certificate requestCertificate(byte[] csr, Instant notBefore, Instant notAfter)
throws AcmeException { throws AcmeException {
Objects.requireNonNull(csr, "csr"); Objects.requireNonNull(csr, "csr");

View File

@ -15,8 +15,9 @@ package org.shredzone.acme4j;
import java.net.URI; import java.net.URI;
import java.security.KeyPair; import java.security.KeyPair;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -48,7 +49,7 @@ public class Session {
private JSON directoryJson; private JSON directoryJson;
private Metadata metadata; private Metadata metadata;
private Locale locale = Locale.getDefault(); private Locale locale = Locale.getDefault();
protected Date directoryCacheExpiry; protected Instant directoryCacheExpiry;
/** /**
* Creates a new {@link Session}. * Creates a new {@link Session}.
@ -210,10 +211,10 @@ public class Session {
*/ */
private void readDirectory() throws AcmeException { private void readDirectory() throws AcmeException {
synchronized (this) { synchronized (this) {
Date now = new Date(); Instant now = Instant.now();
if (directoryJson == null || !directoryCacheExpiry.after(now)) { if (directoryJson == null || !directoryCacheExpiry.isAfter(now)) {
directoryJson = provider().directory(this, getServerUri()); directoryJson = provider().directory(this, getServerUri());
directoryCacheExpiry = new Date(now.getTime() + 60 * 60 * 1000L); directoryCacheExpiry = now.plus(Duration.ofHours(1));
JSON meta = directoryJson.get("meta").asObject(); JSON meta = directoryJson.get("meta").asObject();
if (meta != null) { if (meta != null) {

View File

@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.util.Date; import java.time.Instant;
import java.util.Objects; import java.util.Objects;
import org.shredzone.acme4j.AcmeResource; import org.shredzone.acme4j.AcmeResource;
@ -113,8 +113,8 @@ public class Challenge extends AcmeResource {
/** /**
* Returns the validation date, if returned by the server. * Returns the validation date, if returned by the server.
*/ */
public Date getValidated() { public Instant getValidated() {
return data.get(KEY_VALIDATED).asDate(); return data.get(KEY_VALIDATED).asInstant();
} }
/** /**

View File

@ -27,12 +27,13 @@ import java.security.KeyPair;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -253,9 +254,9 @@ public class DefaultConnection implements Connection {
try { try {
if (conn.getResponseCode() == HttpURLConnection.HTTP_ACCEPTED) { if (conn.getResponseCode() == HttpURLConnection.HTTP_ACCEPTED) {
Date retryAfter = getRetryAfterHeader(); Optional<Instant> retryAfter = getRetryAfterHeader();
if (retryAfter != null) { if (retryAfter.isPresent()) {
throw new AcmeRetryAfterException(message, retryAfter); throw new AcmeRetryAfterException(message, retryAfter.get());
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
@ -338,27 +339,29 @@ public class DefaultConnection implements Connection {
/** /**
* Gets the instant sent with the Retry-After header. * Gets the instant sent with the Retry-After header.
*/ */
private Date getRetryAfterHeader() { private Optional<Instant> getRetryAfterHeader() {
// See RFC 2616 section 14.37 // See RFC 2616 section 14.37
String header = conn.getHeaderField(RETRY_AFTER_HEADER); String header = conn.getHeaderField(RETRY_AFTER_HEADER);
if (header == null) { if (header != null) {
return null; try {
} // delta-seconds
if (header.matches("^\\d+$")) {
int delta = Integer.parseInt(header);
long date = conn.getHeaderFieldDate(DATE_HEADER, System.currentTimeMillis());
return Optional.of(Instant.ofEpochMilli(date).plusSeconds(delta));
}
try { // HTTP-date
// delta-seconds long date = conn.getHeaderFieldDate(RETRY_AFTER_HEADER, 0L);
if (header.matches("^\\d+$")) { if (date != 0) {
int delta = Integer.parseInt(header); return Optional.of(Instant.ofEpochMilli(date));
long date = conn.getHeaderFieldDate(DATE_HEADER, System.currentTimeMillis()); }
return new Date(date + delta * 1000L); } catch (Exception ex) {
throw new AcmeProtocolException("Bad retry-after header value: " + header, ex);
} }
// HTTP-date
long date = conn.getHeaderFieldDate(RETRY_AFTER_HEADER, 0L);
return date != 0 ? new Date(date) : null;
} catch (Exception ex) {
throw new AcmeProtocolException("Bad retry-after header value: " + header, ex);
} }
return Optional.empty();
} }
/** /**
@ -386,9 +389,9 @@ public class DefaultConnection implements Connection {
} }
if ("rateLimited".equals(error)) { if ("rateLimited".equals(error)) {
Date retryAfter = getRetryAfterHeader(); Optional<Instant> retryAfter = getRetryAfterHeader();
Collection<URI> rateLimits = getLinks("rate-limit"); Collection<URI> rateLimits = getLinks("rate-limit");
return new AcmeRateLimitExceededException(type, detail, retryAfter, rateLimits); return new AcmeRateLimitExceededException(type, detail, retryAfter.orElse(null), rateLimits);
} }
return new AcmeServerException(type, detail); return new AcmeServerException(type, detail);

View File

@ -14,9 +14,9 @@
package org.shredzone.acme4j.exception; package org.shredzone.acme4j.exception;
import java.net.URI; import java.net.URI;
import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
/** /**
* An exception that is thrown when a rate limit was exceeded. * An exception that is thrown when a rate limit was exceeded.
@ -24,7 +24,7 @@ import java.util.Date;
public class AcmeRateLimitExceededException extends AcmeServerException { public class AcmeRateLimitExceededException extends AcmeServerException {
private static final long serialVersionUID = 4150484059796413069L; private static final long serialVersionUID = 4150484059796413069L;
private final Date retryAfter; private final Instant retryAfter;
private final Collection<URI> documents; private final Collection<URI> documents;
/** /**
@ -41,7 +41,7 @@ public class AcmeRateLimitExceededException extends AcmeServerException {
* @param documents * @param documents
* URIs pointing to documents about the rate limit that was hit * URIs pointing to documents about the rate limit that was hit
*/ */
public AcmeRateLimitExceededException(String type, String detail, Date retryAfter, Collection<URI> documents) { public AcmeRateLimitExceededException(String type, String detail, Instant retryAfter, Collection<URI> documents) {
super(type, detail); super(type, detail);
this.retryAfter = retryAfter; this.retryAfter = retryAfter;
this.documents = this.documents =
@ -52,8 +52,8 @@ public class AcmeRateLimitExceededException extends AcmeServerException {
* Returns the moment the request is expected to succeed again. {@code null} if this * Returns the moment the request is expected to succeed again. {@code null} if this
* moment is not known. * moment is not known.
*/ */
public Date getRetryAfter() { public Instant getRetryAfter() {
return retryAfter != null ? new Date(retryAfter.getTime()) : null; return retryAfter;
} }
/** /**

View File

@ -13,7 +13,7 @@
*/ */
package org.shredzone.acme4j.exception; package org.shredzone.acme4j.exception;
import java.util.Date; import java.time.Instant;
import java.util.Objects; import java.util.Objects;
/** /**
@ -23,7 +23,7 @@ import java.util.Objects;
public class AcmeRetryAfterException extends AcmeException { public class AcmeRetryAfterException extends AcmeException {
private static final long serialVersionUID = 4461979121063649905L; private static final long serialVersionUID = 4461979121063649905L;
private final Date retryAfter; private final Instant retryAfter;
/** /**
* Creates a new {@link AcmeRetryAfterException}. * Creates a new {@link AcmeRetryAfterException}.
@ -33,7 +33,7 @@ public class AcmeRetryAfterException extends AcmeException {
* @param retryAfter * @param retryAfter
* retry-after date returned by the server * retry-after date returned by the server
*/ */
public AcmeRetryAfterException(String msg, Date retryAfter) { public AcmeRetryAfterException(String msg, Instant retryAfter) {
super(msg); super(msg);
this.retryAfter = Objects.requireNonNull(retryAfter); this.retryAfter = Objects.requireNonNull(retryAfter);
} }
@ -41,8 +41,8 @@ public class AcmeRetryAfterException extends AcmeException {
/** /**
* Returns the retry-after date returned by the server. * Returns the retry-after date returned by the server.
*/ */
public Date getRetryAfter() { public Instant getRetryAfter() {
return new Date(retryAfter.getTime()); return retryAfter;
} }
} }

View File

@ -17,10 +17,9 @@ import java.io.UnsupportedEncodingException;
import java.net.IDN; import java.net.IDN;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Calendar; import java.time.Instant;
import java.util.Date; import java.time.ZoneId;
import java.util.GregorianCalendar; import java.time.ZonedDateTime;
import java.util.TimeZone;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -164,12 +163,12 @@ public final class AcmeUtils {
* *
* @param str * @param str
* Date string * Date string
* @return {@link Date} that was parsed * @return {@link Instant} that was parsed
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if the date string was not RFC 3339 formatted * if the date string was not RFC 3339 formatted
* @see <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC 3339</a> * @see <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC 3339</a>
*/ */
public static Date parseTimestamp(String str) { public static Instant parseTimestamp(String str) {
Matcher m = DATE_PATTERN.matcher(str); Matcher m = DATE_PATTERN.matcher(str);
if (!m.matches()) { if (!m.matches()) {
throw new IllegalArgumentException("Illegal date: " + str); throw new IllegalArgumentException("Illegal date: " + str);
@ -198,11 +197,9 @@ public final class AcmeUtils {
tz = TZ_PATTERN.matcher(tz).replaceAll("GMT$1$2:$3"); tz = TZ_PATTERN.matcher(tz).replaceAll("GMT$1$2:$3");
} }
Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(tz)); return ZonedDateTime.of(
cal.clear(); year, month, dom, hour, minute, second, ms * 1_000_000,
cal.set(year, month - 1, dom, hour, minute, second); ZoneId.of(tz)).toInstant();
cal.set(Calendar.MILLISECOND, ms);
return cal.getTime();
} }
/** /**

View File

@ -27,8 +27,8 @@ import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -349,12 +349,11 @@ public final class JSON implements Serializable {
} }
/** /**
* Returns the value as {@link Date}. * Returns the value as {@link Instant}.
* *
* @return {@link Date}, or {@code null} if the value was not set. The returned * @return {@link Instant}, or {@code null} if the value was not set.
* {@link Date} object is not shared and can be modified safely.
*/ */
public Date asDate() { public Instant asInstant() {
if (val == null) { if (val == null) {
return null; return null;
} }

View File

@ -17,12 +17,11 @@ import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode;
import java.security.Key; import java.security.Key;
import java.security.PublicKey; import java.security.PublicKey;
import java.text.SimpleDateFormat; import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.TimeZone;
import java.util.TreeMap; import java.util.TreeMap;
import org.jose4j.json.JsonUtil; import org.jose4j.json.JsonUtil;
@ -62,25 +61,22 @@ public class JSONBuilder {
} }
/** /**
* Puts a {@link Date} to the JSON. If a property with the key exists, it will be * Puts an {@link Instant} to the JSON. If a property with the key exists, it will be
* replaced. * replaced.
* *
* @param key * @param key
* Property key * Property key
* @param value * @param value
* Property {@link Date} value * Property {@link Instant} value
* @return {@code this} * @return {@code this}
*/ */
public JSONBuilder put(String key, Date value) { public JSONBuilder put(String key, Instant value) {
if (value == null) { if (value == null) {
put(key, (Object) null); put(key, (Object) null);
return this; return this;
} }
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); put(key, DateTimeFormatter.ISO_INSTANT.format(value));
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = fmt.format(value);
put(key, date);
return this; return this;
} }

View File

@ -21,8 +21,9 @@ import static org.shredzone.acme4j.util.TestUtils.*;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test; import org.junit.Test;
@ -228,7 +229,7 @@ public class AuthorizationTest {
*/ */
@Test @Test
public void testUpdateRetryAfter() throws Exception { public void testUpdateRetryAfter() throws Exception {
final long retryAfter = System.currentTimeMillis() + 30 * 1000L; final Instant retryAfter = Instant.now().plus(Duration.ofSeconds(30));
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
@ -250,7 +251,7 @@ public class AuthorizationTest {
@Override @Override
public void handleRetryAfter(String message) throws AcmeException { public void handleRetryAfter(String message) throws AcmeException {
throw new AcmeRetryAfterException(message, new Date(retryAfter)); throw new AcmeRetryAfterException(message, retryAfter);
} }
}; };
@ -267,7 +268,7 @@ public class AuthorizationTest {
auth.update(); auth.update();
fail("Expected AcmeRetryAfterException"); fail("Expected AcmeRetryAfterException");
} catch (AcmeRetryAfterException ex) { } catch (AcmeRetryAfterException ex) {
assertThat(ex.getRetryAfter(), is(new Date(retryAfter))); assertThat(ex.getRetryAfter(), is(retryAfter));
} }
assertThat(auth.getDomain(), is("example.org")); assertThat(auth.getDomain(), is("example.org"));

View File

@ -22,7 +22,8 @@ import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import org.junit.Test; import org.junit.Test;
import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.connector.Resource;
@ -107,7 +108,7 @@ public class CertificateTest {
*/ */
@Test @Test
public void testRetryAfter() throws AcmeException, IOException { public void testRetryAfter() throws AcmeException, IOException {
final long retryAfter = System.currentTimeMillis() + 30 * 1000L; final Instant retryAfter = Instant.now().plus(Duration.ofSeconds(30));
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
@ -125,7 +126,7 @@ public class CertificateTest {
@Override @Override
public void handleRetryAfter(String message) throws AcmeException { public void handleRetryAfter(String message) throws AcmeException {
throw new AcmeRetryAfterException(message, new Date(retryAfter)); throw new AcmeRetryAfterException(message, retryAfter);
} }
}; };
@ -135,7 +136,7 @@ public class CertificateTest {
cert.download(); cert.download();
fail("Expected AcmeRetryAfterException"); fail("Expected AcmeRetryAfterException");
} catch (AcmeRetryAfterException ex) { } catch (AcmeRetryAfterException ex) {
assertThat(ex.getRetryAfter(), is(new Date(retryAfter))); assertThat(ex.getRetryAfter(), is(retryAfter));
} }
provider.close(); provider.close();

View File

@ -23,9 +23,10 @@ import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Calendar; import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Iterator; import java.util.Iterator;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.jose4j.jws.JsonWebSignature; import org.jose4j.jws.JsonWebSignature;
@ -335,15 +336,12 @@ public class RegistrationTest {
provider.putTestResource(Resource.NEW_CERT, resourceUri); provider.putTestResource(Resource.NEW_CERT, resourceUri);
byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der");
Calendar notBefore = Calendar.getInstance(TimeZone.getTimeZone("UTC")); ZoneId utc = ZoneId.of("UTC");
notBefore.clear(); Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant();
notBefore.set(2016, Calendar.JANUARY, 1); Instant notAfter = LocalDate.of(2016, 1, 8).atStartOfDay(utc).toInstant();
Calendar notAfter = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
notAfter.clear();
notAfter.set(2016, Calendar.JANUARY, 8);
Registration registration = new Registration(provider.createSession(), locationUri); Registration registration = new Registration(provider.createSession(), locationUri);
Certificate cert = registration.requestCertificate(csr, notBefore.getTime(), notAfter.getTime()); Certificate cert = registration.requestCertificate(csr, notBefore, notAfter);
assertThat(cert.download(), is(originalCert)); assertThat(cert.download(), is(originalCert));
assertThat(cert.getLocation(), is(locationUri)); assertThat(cert.getLocation(), is(locationUri));

View File

@ -21,7 +21,7 @@ import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.security.KeyPair; import java.security.KeyPair;
import java.util.Date; import java.time.Instant;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentMatchers; import org.mockito.ArgumentMatchers;
@ -173,7 +173,7 @@ public class SessionTest {
ArgumentMatchers.any(URI.class)); ArgumentMatchers.any(URI.class));
// Simulate a cache expiry // Simulate a cache expiry
session.directoryCacheExpiry = new Date(); session.directoryCacheExpiry = Instant.now();
// Make sure directory is read once again // Make sure directory is read once again
assertSession(session); assertSession(session);

View File

@ -25,7 +25,8 @@ import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import org.jose4j.lang.JoseException; import org.jose4j.lang.JoseException;
import org.junit.Before; import org.junit.Before;
@ -229,7 +230,7 @@ public class ChallengeTest {
*/ */
@Test @Test
public void testUpdateRetryAfter() throws Exception { public void testUpdateRetryAfter() throws Exception {
final long retryAfter = System.currentTimeMillis() + 30 * 1000L; final Instant retryAfter = Instant.now().plus(Duration.ofSeconds(30));
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
@ -252,7 +253,7 @@ public class ChallengeTest {
@Override @Override
public void handleRetryAfter(String message) throws AcmeException { public void handleRetryAfter(String message) throws AcmeException {
throw new AcmeRetryAfterException(message, new Date(retryAfter)); throw new AcmeRetryAfterException(message, retryAfter);
} }
}; };
@ -265,7 +266,7 @@ public class ChallengeTest {
challenge.update(); challenge.update();
fail("Expected AcmeRetryAfterException"); fail("Expected AcmeRetryAfterException");
} catch (AcmeRetryAfterException ex) { } catch (AcmeRetryAfterException ex) {
assertThat(ex.getRetryAfter(), is(new Date(retryAfter))); assertThat(ex.getRetryAfter(), is(retryAfter));
} }
assertThat(challenge.getStatus(), is(Status.VALID)); assertThat(challenge.getStatus(), is(Status.VALID));

View File

@ -25,9 +25,10 @@ import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -254,12 +255,12 @@ public class DefaultConnectionTest {
*/ */
@Test @Test
public void testHandleRetryAfterHeaderDate() throws AcmeException, IOException { public void testHandleRetryAfterHeaderDate() throws AcmeException, IOException {
Date retryDate = new Date(System.currentTimeMillis() + 10 * 60 * 60 * 1000L); Instant retryDate = Instant.now().plus(Duration.ofHours(10));
String retryMsg = "absolute date"; String retryMsg = "absolute date";
when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_ACCEPTED); when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_ACCEPTED);
when(mockUrlConnection.getHeaderField("Retry-After")).thenReturn(retryDate.toString()); when(mockUrlConnection.getHeaderField("Retry-After")).thenReturn(retryDate.toString());
when(mockUrlConnection.getHeaderFieldDate("Retry-After", 0L)).thenReturn(retryDate.getTime()); when(mockUrlConnection.getHeaderFieldDate("Retry-After", 0L)).thenReturn(retryDate.toEpochMilli());
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection; conn.conn = mockUrlConnection;
@ -297,7 +298,7 @@ public class DefaultConnectionTest {
conn.handleRetryAfter(retryMsg); conn.handleRetryAfter(retryMsg);
fail("no AcmeRetryAfterException was thrown"); fail("no AcmeRetryAfterException was thrown");
} catch (AcmeRetryAfterException ex) { } catch (AcmeRetryAfterException ex) {
assertThat(ex.getRetryAfter(), is(new Date(now + delta * 1000L))); assertThat(ex.getRetryAfter(), is(Instant.ofEpochMilli(now).plusSeconds(delta)));
assertThat(ex.getMessage(), is(retryMsg)); assertThat(ex.getMessage(), is(retryMsg));
} }

View File

@ -17,9 +17,10 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.net.URI; import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import org.junit.Test; import org.junit.Test;
@ -35,7 +36,7 @@ public class AcmeRateLimitExceededExceptionTest {
public void testAcmeRateLimitExceededException() { public void testAcmeRateLimitExceededException() {
String type = "urn:ietf:params:acme:error:rateLimited"; String type = "urn:ietf:params:acme:error:rateLimited";
String detail = "Too many requests per minute"; String detail = "Too many requests per minute";
Date retryAfter = new Date(System.currentTimeMillis() + 60 * 1000L); Instant retryAfter = Instant.now().plus(Duration.ofMinutes(1));
Collection<URI> documents = Arrays.asList( Collection<URI> documents = Arrays.asList(
URI.create("http://example.com/doc1.html"), URI.create("http://example.com/doc1.html"),
URI.create("http://example.com/doc2.html")); URI.create("http://example.com/doc2.html"));

View File

@ -16,7 +16,8 @@ package org.shredzone.acme4j.exception;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.util.Date; import java.time.Duration;
import java.time.Instant;
import org.junit.Test; import org.junit.Test;
@ -31,16 +32,12 @@ public class AcmeRetryAfterExceptionTest {
@Test @Test
public void testAcmeRetryAfterException() { public void testAcmeRetryAfterException() {
String detail = "Too early"; String detail = "Too early";
Date retryAfter = new Date(System.currentTimeMillis() + 60 * 1000L); Instant retryAfter = Instant.now().plus(Duration.ofMinutes(1));
AcmeRetryAfterException ex AcmeRetryAfterException ex = new AcmeRetryAfterException(detail, retryAfter);
= new AcmeRetryAfterException(detail, retryAfter);
assertThat(ex.getMessage(), is(detail)); assertThat(ex.getMessage(), is(detail));
assertThat(ex.getRetryAfter(), is(retryAfter)); assertThat(ex.getRetryAfter(), is(retryAfter));
// make sure we get a copy of the Date object
assertThat(ex.getRetryAfter(), not(sameInstance(retryAfter)));
} }
/** /**
@ -48,16 +45,12 @@ public class AcmeRetryAfterExceptionTest {
*/ */
@Test @Test
public void testNullAcmeRetryAfterException() { public void testNullAcmeRetryAfterException() {
Date retryAfter = new Date(System.currentTimeMillis() + 60 * 1000L); Instant retryAfter = Instant.now().plus(Duration.ofMinutes(1));
AcmeRetryAfterException ex AcmeRetryAfterException ex = new AcmeRetryAfterException(null, retryAfter);
= new AcmeRetryAfterException(null, retryAfter);
assertThat(ex.getMessage(), nullValue()); assertThat(ex.getMessage(), nullValue());
assertThat(ex.getRetryAfter(), is(retryAfter)); assertThat(ex.getRetryAfter(), is(retryAfter));
// make sure we get a copy of the Date object
assertThat(ex.getRetryAfter(), not(sameInstance(retryAfter)));
} }
/** /**

View File

@ -21,11 +21,10 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.Security; import java.security.Security;
import java.text.SimpleDateFormat; import java.time.Instant;
import java.util.Calendar; import java.time.ZoneId;
import java.util.Date; import java.time.ZonedDateTime;
import java.util.Locale; import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
import javax.xml.bind.DatatypeConverter; import javax.xml.bind.DatatypeConverter;
@ -247,57 +246,55 @@ public class AcmeUtilsTest {
/** /**
* Matches the given time. * Matches the given time.
*/ */
private DateMatcher isDate(int year, int month, int dom, int hour, int minute, int second) { private InstantMatcher isDate(int year, int month, int dom, int hour, int minute, int second) {
return isDate(year, month, dom, hour, minute, second, 0); return isDate(year, month, dom, hour, minute, second, 0);
} }
/** /**
* Matches the given time and milliseconds. * Matches the given time and milliseconds.
*/ */
private DateMatcher isDate(int year, int month, int dom, int hour, int minute, int second, int ms) { private InstantMatcher isDate(int year, int month, int dom, int hour, int minute, int second, int ms) {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Instant cmp = ZonedDateTime.of(
cal.clear(); year, month, dom, hour, minute, second, ms * 1_000_000,
cal.set(year, month - 1, dom, hour, minute, second); ZoneId.of("UTC")).toInstant();
cal.set(Calendar.MILLISECOND, ms); return new InstantMatcher(cmp);
return new DateMatcher(cal);
} }
/** /**
* Date matcher that gives a readable output on mismatch. * Date matcher that gives a readable output on mismatch.
*/ */
private static class DateMatcher extends BaseMatcher<Date> { private static class InstantMatcher extends BaseMatcher<Instant> {
private final Calendar cal; private final Instant cmp;
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); private final DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
public DateMatcher(Calendar cal) { public InstantMatcher(Instant cmp) {
this.cal = cal; this.cmp = cmp;
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
} }
@Override @Override
public boolean matches(Object item) { public boolean matches(Object item) {
if (!(item instanceof Date)) { if (!(item instanceof Instant)) {
return false; return false;
} }
Date date = (Date) item; Instant date = (Instant) item;
return date.equals(cal.getTime()); return date.equals(cmp);
} }
@Override @Override
public void describeTo(Description description) { public void describeTo(Description description) {
description.appendValue(sdf.format(cal.getTime())); description.appendValue(dtf.format(cmp));
} }
@Override @Override
public void describeMismatch(Object item, Description description) { public void describeMismatch(Object item, Description description) {
if (!(item instanceof Date)) { if (!(item instanceof Instant)) {
description.appendText("is not a Date"); description.appendText("is not an Instant");
return; return;
} }
Date date = (Date) item; Instant date = (Instant) item;
description.appendText("was ").appendValue(sdf.format(date)); description.appendText("was ").appendValue(dtf.format(date));
} }
} }

View File

@ -19,10 +19,10 @@ import static org.junit.Assert.assertThat;
import java.io.IOException; import java.io.IOException;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Calendar; import java.time.Instant;
import java.util.Date; import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Map; import java.util.Map;
import java.util.TimeZone;
import org.jose4j.json.JsonUtil; import org.jose4j.json.JsonUtil;
import org.jose4j.lang.JoseException; import org.jose4j.lang.JoseException;
@ -81,13 +81,11 @@ public class JSONBuilderTest {
*/ */
@Test @Test
public void testDate() { public void testDate() {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+2")); Instant date = ZonedDateTime.of(2016, 6, 1, 5, 13, 46, 0, ZoneId.of("GMT+2")).toInstant();
cal.clear();
cal.set(2016, Calendar.JUNE, 1, 5, 13, 46);
JSONBuilder cb = new JSONBuilder(); JSONBuilder cb = new JSONBuilder();
cb.put("fooDate", cal.getTime()); cb.put("fooDate", date);
cb.put("fooNull", (Date) null); cb.put("fooNull", (Instant) null);
assertThat(cb.toString(), is("{\"fooDate\":\"2016-06-01T03:13:46Z\",\"fooNull\":null}")); assertThat(cb.toString(), is("{\"fooDate\":\"2016-06-01T03:13:46Z\",\"fooNull\":null}"));
} }

View File

@ -26,10 +26,11 @@ import java.io.ObjectOutputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.Calendar; import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.TimeZone;
import org.junit.Test; import org.junit.Test;
import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeProtocolException;
@ -145,9 +146,7 @@ public class JSONTest {
*/ */
@Test @Test
public void testGetter() throws MalformedURLException { public void testGetter() throws MalformedURLException {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Instant date = LocalDate.of(2016, 1, 8).atStartOfDay(ZoneId.of("UTC")).toInstant();
cal.clear();
cal.set(2016, 0, 8);
JSON json = TestUtils.getJsonAsObject("json"); JSON json = TestUtils.getJsonAsObject("json");
@ -155,7 +154,7 @@ public class JSONTest {
assertThat(json.get("number").asInt(), is(123)); assertThat(json.get("number").asInt(), is(123));
assertThat(json.get("uri").asURI(), is(URI.create("mailto:foo@example.com"))); assertThat(json.get("uri").asURI(), is(URI.create("mailto:foo@example.com")));
assertThat(json.get("url").asURL(), is(new URL("http://example.com"))); assertThat(json.get("url").asURL(), is(new URL("http://example.com")));
assertThat(json.get("date").asDate(), is(cal.getTime())); assertThat(json.get("date").asInstant(), is(date));
JSON.Array array = json.get("array").asArray(); JSON.Array array = json.get("array").asArray();
assertThat(array.get(0).asString(), is("foo")); assertThat(array.get(0).asString(), is("foo"));
@ -181,7 +180,7 @@ public class JSONTest {
assertThat(json.get("none").asString(), is(nullValue())); assertThat(json.get("none").asString(), is(nullValue()));
assertThat(json.get("none").asURI(), is(nullValue())); assertThat(json.get("none").asURI(), is(nullValue()));
assertThat(json.get("none").asURL(), is(nullValue())); assertThat(json.get("none").asURL(), is(nullValue()));
assertThat(json.get("none").asDate(), is(nullValue())); assertThat(json.get("none").asInstant(), is(nullValue()));
assertThat(json.get("none").asArray(), is(nullValue())); assertThat(json.get("none").asArray(), is(nullValue()));
assertThat(json.get("none").asObject(), is(nullValue())); assertThat(json.get("none").asObject(), is(nullValue()));
@ -246,7 +245,7 @@ public class JSONTest {
} }
try { try {
json.get("text").asDate(); json.get("text").asInstant();
fail("no exception was thrown"); fail("no exception was thrown");
} catch (AcmeProtocolException ex) { } catch (AcmeProtocolException ex) {
// expected // expected

View File

@ -25,6 +25,8 @@ import java.security.KeyPair;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500Name;
@ -212,17 +214,17 @@ public final class CertificateUtils {
*/ */
private static X509Certificate createCertificate(KeyPair keypair, String... subject) throws IOException { private static X509Certificate createCertificate(KeyPair keypair, String... subject) throws IOException {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
final long validSpanMs = 7 * 24 * 60 * 60 * 1000L;
final String signatureAlg = "SHA256withRSA"; final String signatureAlg = "SHA256withRSA";
try { try {
X500Name issuer = new X500Name("CN=acme.invalid"); X500Name issuer = new X500Name("CN=acme.invalid");
BigInteger serial = BigInteger.valueOf(now); BigInteger serial = BigInteger.valueOf(now);
Date notBefore = new Date(now); Instant notBefore = Instant.ofEpochMilli(now);
Date notAfter = new Date(now + validSpanMs); Instant notAfter = notBefore.plus(Duration.ofDays(7));
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder( JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
issuer, serial, notBefore, notAfter, issuer, keypair.getPublic()); issuer, serial, Date.from(notBefore), Date.from(notAfter),
issuer, keypair.getPublic());
GeneralName[] gns = new GeneralName[subject.length]; GeneralName[] gns = new GeneralName[subject.length];
for (int ix = 0; ix < subject.length; ix++) { for (int ix = 0; ix < subject.length; ix++) {

View File

@ -28,6 +28,8 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException; import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -132,13 +134,13 @@ public class CertificateUtilsTest {
X509Certificate cert = CertificateUtils.createTlsSniCertificate(keypair, subject); X509Certificate cert = CertificateUtils.createTlsSniCertificate(keypair, subject);
Date now = new Date(); Instant now = Instant.now();
Date end = new Date(now.getTime() + (8 * 24 * 60 * 60 * 1000L)); Instant end = now.plus(Duration.ofDays(8));
assertThat(cert, not(nullValue())); assertThat(cert, not(nullValue()));
assertThat(cert.getNotAfter(), is(greaterThan(now))); assertThat(cert.getNotAfter(), is(greaterThan(Date.from(now))));
assertThat(cert.getNotAfter(), is(lessThan(end))); assertThat(cert.getNotAfter(), is(lessThan(Date.from(end))));
assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(now))); assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(Date.from(now))));
assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid")); assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid"));
assertThat(getSANs(cert), containsInAnyOrder(subject)); assertThat(getSANs(cert), containsInAnyOrder(subject));
} }
@ -156,13 +158,13 @@ public class CertificateUtilsTest {
X509Certificate cert = CertificateUtils.createTlsSni02Certificate(keypair, sanA, sanB); X509Certificate cert = CertificateUtils.createTlsSni02Certificate(keypair, sanA, sanB);
Date now = new Date(); Instant now = Instant.now();
Date end = new Date(now.getTime() + (8 * 24 * 60 * 60 * 1000L)); Instant end = now.plus(Duration.ofDays(8));
assertThat(cert, not(nullValue())); assertThat(cert, not(nullValue()));
assertThat(cert.getNotAfter(), is(greaterThan(now))); assertThat(cert.getNotAfter(), is(greaterThan(Date.from(now))));
assertThat(cert.getNotAfter(), is(lessThan(end))); assertThat(cert.getNotAfter(), is(lessThan(Date.from(end))));
assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(now))); assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(Date.from(now))));
assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid")); assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid"));
assertThat(getSANs(cert), containsInAnyOrder(sanA, sanB)); assertThat(getSANs(cert), containsInAnyOrder(sanA, sanB));
} }