diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java index 56a91588..146990b7 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java @@ -17,11 +17,11 @@ import static org.shredzone.acme4j.util.AcmeUtils.parseTimestamp; import java.net.HttpURLConnection; import java.net.URI; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.List; import org.shredzone.acme4j.challenge.Challenge; @@ -43,7 +43,7 @@ public class Authorization extends AcmeResource { private String domain; private Status status; - private Date expires; + private Instant expires; private List challenges; private List> combinations; 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. */ - public Date getExpires() { + public Instant getExpires() { load(); return expires; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java index e873b4c6..00a5fd81 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java @@ -19,10 +19,10 @@ import java.net.HttpURLConnection; import java.net.URI; import java.security.KeyPair; import java.security.cert.X509Certificate; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -234,7 +234,7 @@ public class Registration extends AcmeResource { * for default. May be ignored by the server. * @return The {@link Certificate} */ - public Certificate requestCertificate(byte[] csr, Date notBefore, Date notAfter) + public Certificate requestCertificate(byte[] csr, Instant notBefore, Instant notAfter) throws AcmeException { Objects.requireNonNull(csr, "csr"); diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java index a6131bdf..f1424e8d 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java @@ -15,8 +15,9 @@ package org.shredzone.acme4j; import java.net.URI; import java.security.KeyPair; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; -import java.util.Date; import java.util.EnumMap; import java.util.List; import java.util.Locale; @@ -48,7 +49,7 @@ public class Session { private JSON directoryJson; private Metadata metadata; private Locale locale = Locale.getDefault(); - protected Date directoryCacheExpiry; + protected Instant directoryCacheExpiry; /** * Creates a new {@link Session}. @@ -210,10 +211,10 @@ public class Session { */ private void readDirectory() throws AcmeException { synchronized (this) { - Date now = new Date(); - if (directoryJson == null || !directoryCacheExpiry.after(now)) { + Instant now = Instant.now(); + if (directoryJson == null || !directoryCacheExpiry.isAfter(now)) { 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(); if (meta != null) { diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java index e062ccf8..5e235f61 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge; import java.net.HttpURLConnection; import java.net.URI; -import java.util.Date; +import java.time.Instant; import java.util.Objects; import org.shredzone.acme4j.AcmeResource; @@ -113,8 +113,8 @@ public class Challenge extends AcmeResource { /** * Returns the validation date, if returned by the server. */ - public Date getValidated() { - return data.get(KEY_VALIDATED).asDate(); + public Instant getValidated() { + return data.get(KEY_VALIDATED).asInstant(); } /** diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java index f4e6046f..6ba604f5 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java @@ -27,12 +27,13 @@ import java.security.KeyPair; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Date; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.OptionalInt; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -253,9 +254,9 @@ public class DefaultConnection implements Connection { try { if (conn.getResponseCode() == HttpURLConnection.HTTP_ACCEPTED) { - Date retryAfter = getRetryAfterHeader(); - if (retryAfter != null) { - throw new AcmeRetryAfterException(message, retryAfter); + Optional retryAfter = getRetryAfterHeader(); + if (retryAfter.isPresent()) { + throw new AcmeRetryAfterException(message, retryAfter.get()); } } } catch (IOException ex) { @@ -338,27 +339,29 @@ public class DefaultConnection implements Connection { /** * Gets the instant sent with the Retry-After header. */ - private Date getRetryAfterHeader() { + private Optional getRetryAfterHeader() { // See RFC 2616 section 14.37 String header = conn.getHeaderField(RETRY_AFTER_HEADER); - if (header == null) { - return null; - } + if (header != 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 { - // delta-seconds - if (header.matches("^\\d+$")) { - int delta = Integer.parseInt(header); - long date = conn.getHeaderFieldDate(DATE_HEADER, System.currentTimeMillis()); - return new Date(date + delta * 1000L); + // HTTP-date + long date = conn.getHeaderFieldDate(RETRY_AFTER_HEADER, 0L); + if (date != 0) { + return Optional.of(Instant.ofEpochMilli(date)); + } + } 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)) { - Date retryAfter = getRetryAfterHeader(); + Optional retryAfter = getRetryAfterHeader(); Collection 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); diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededException.java b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededException.java index 24b4a185..66792440 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededException.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededException.java @@ -14,9 +14,9 @@ package org.shredzone.acme4j.exception; import java.net.URI; +import java.time.Instant; import java.util.Collection; import java.util.Collections; -import java.util.Date; /** * An exception that is thrown when a rate limit was exceeded. @@ -24,7 +24,7 @@ import java.util.Date; public class AcmeRateLimitExceededException extends AcmeServerException { private static final long serialVersionUID = 4150484059796413069L; - private final Date retryAfter; + private final Instant retryAfter; private final Collection documents; /** @@ -41,7 +41,7 @@ public class AcmeRateLimitExceededException extends AcmeServerException { * @param documents * URIs pointing to documents about the rate limit that was hit */ - public AcmeRateLimitExceededException(String type, String detail, Date retryAfter, Collection documents) { + public AcmeRateLimitExceededException(String type, String detail, Instant retryAfter, Collection documents) { super(type, detail); this.retryAfter = retryAfter; 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 * moment is not known. */ - public Date getRetryAfter() { - return retryAfter != null ? new Date(retryAfter.getTime()) : null; + public Instant getRetryAfter() { + return retryAfter; } /** diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRetryAfterException.java b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRetryAfterException.java index 08c3731b..19095073 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRetryAfterException.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRetryAfterException.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.exception; -import java.util.Date; +import java.time.Instant; import java.util.Objects; /** @@ -23,7 +23,7 @@ import java.util.Objects; public class AcmeRetryAfterException extends AcmeException { private static final long serialVersionUID = 4461979121063649905L; - private final Date retryAfter; + private final Instant retryAfter; /** * Creates a new {@link AcmeRetryAfterException}. @@ -33,7 +33,7 @@ public class AcmeRetryAfterException extends AcmeException { * @param retryAfter * retry-after date returned by the server */ - public AcmeRetryAfterException(String msg, Date retryAfter) { + public AcmeRetryAfterException(String msg, Instant retryAfter) { super(msg); this.retryAfter = Objects.requireNonNull(retryAfter); } @@ -41,8 +41,8 @@ public class AcmeRetryAfterException extends AcmeException { /** * Returns the retry-after date returned by the server. */ - public Date getRetryAfter() { - return new Date(retryAfter.getTime()); + public Instant getRetryAfter() { + return retryAfter; } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/AcmeUtils.java b/acme4j-client/src/main/java/org/shredzone/acme4j/util/AcmeUtils.java index c0d11a47..dedd9d9a 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/AcmeUtils.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/util/AcmeUtils.java @@ -17,10 +17,9 @@ import java.io.UnsupportedEncodingException; import java.net.IDN; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -164,12 +163,12 @@ public final class AcmeUtils { * * @param str * Date string - * @return {@link Date} that was parsed + * @return {@link Instant} that was parsed * @throws IllegalArgumentException * if the date string was not RFC 3339 formatted * @see RFC 3339 */ - public static Date parseTimestamp(String str) { + public static Instant parseTimestamp(String str) { Matcher m = DATE_PATTERN.matcher(str); if (!m.matches()) { throw new IllegalArgumentException("Illegal date: " + str); @@ -198,11 +197,9 @@ public final class AcmeUtils { tz = TZ_PATTERN.matcher(tz).replaceAll("GMT$1$2:$3"); } - Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(tz)); - cal.clear(); - cal.set(year, month - 1, dom, hour, minute, second); - cal.set(Calendar.MILLISECOND, ms); - return cal.getTime(); + return ZonedDateTime.of( + year, month, dom, hour, minute, second, ms * 1_000_000, + ZoneId.of(tz)).toInstant(); } /** diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java index 9fee4837..50cc2128 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java @@ -27,8 +27,8 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.time.Instant; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.Iterator; 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 - * {@link Date} object is not shared and can be modified safely. + * @return {@link Instant}, or {@code null} if the value was not set. */ - public Date asDate() { + public Instant asInstant() { if (val == null) { return null; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSONBuilder.java b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSONBuilder.java index 246556f6..f4a3874e 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSONBuilder.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSONBuilder.java @@ -17,12 +17,11 @@ import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode; import java.security.Key; import java.security.PublicKey; -import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.format.DateTimeFormatter; import java.util.Collections; -import java.util.Date; import java.util.Map; import java.util.Objects; -import java.util.TimeZone; import java.util.TreeMap; 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. * * @param key * Property key * @param value - * Property {@link Date} value + * Property {@link Instant} value * @return {@code this} */ - public JSONBuilder put(String key, Date value) { + public JSONBuilder put(String key, Instant value) { if (value == null) { put(key, (Object) null); return this; } - SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - fmt.setTimeZone(TimeZone.getTimeZone("UTC")); - String date = fmt.format(value); - put(key, date); + put(key, DateTimeFormatter.ISO_INSTANT.format(value)); return this; } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java index b6cddb34..09db84b7 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java @@ -21,8 +21,9 @@ import static org.shredzone.acme4j.util.TestUtils.*; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; -import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; @@ -228,7 +229,7 @@ public class AuthorizationTest { */ @Test 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() { @Override @@ -250,7 +251,7 @@ public class AuthorizationTest { @Override 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(); fail("Expected AcmeRetryAfterException"); } catch (AcmeRetryAfterException ex) { - assertThat(ex.getRetryAfter(), is(new Date(retryAfter))); + assertThat(ex.getRetryAfter(), is(retryAfter)); } assertThat(auth.getDomain(), is("example.org")); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java index 712f003c..49e4b4a7 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java @@ -22,7 +22,8 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.security.cert.X509Certificate; -import java.util.Date; +import java.time.Duration; +import java.time.Instant; import org.junit.Test; import org.shredzone.acme4j.connector.Resource; @@ -107,7 +108,7 @@ public class CertificateTest { */ @Test 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() { @Override @@ -125,7 +126,7 @@ public class CertificateTest { @Override 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(); fail("Expected AcmeRetryAfterException"); } catch (AcmeRetryAfterException ex) { - assertThat(ex.getRetryAfter(), is(new Date(retryAfter))); + assertThat(ex.getRetryAfter(), is(retryAfter)); } provider.close(); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java index 9b81c7e0..a0624eb0 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java @@ -23,9 +23,10 @@ import java.net.HttpURLConnection; import java.net.URI; import java.security.KeyPair; 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.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; import org.jose4j.jws.JsonWebSignature; @@ -335,15 +336,12 @@ public class RegistrationTest { provider.putTestResource(Resource.NEW_CERT, resourceUri); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); - Calendar notBefore = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - notBefore.clear(); - notBefore.set(2016, Calendar.JANUARY, 1); - Calendar notAfter = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - notAfter.clear(); - notAfter.set(2016, Calendar.JANUARY, 8); + ZoneId utc = ZoneId.of("UTC"); + Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant(); + Instant notAfter = LocalDate.of(2016, 1, 8).atStartOfDay(utc).toInstant(); 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.getLocation(), is(locationUri)); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java index 73eb9033..46311ad6 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java @@ -21,7 +21,7 @@ import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; import java.io.IOException; import java.net.URI; import java.security.KeyPair; -import java.util.Date; +import java.time.Instant; import org.junit.Test; import org.mockito.ArgumentMatchers; @@ -173,7 +173,7 @@ public class SessionTest { ArgumentMatchers.any(URI.class)); // Simulate a cache expiry - session.directoryCacheExpiry = new Date(); + session.directoryCacheExpiry = Instant.now(); // Make sure directory is read once again assertSession(session); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java index efab1f14..68ffe4ae 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java @@ -25,7 +25,8 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.util.Date; +import java.time.Duration; +import java.time.Instant; import org.jose4j.lang.JoseException; import org.junit.Before; @@ -229,7 +230,7 @@ public class ChallengeTest { */ @Test 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() { @Override @@ -252,7 +253,7 @@ public class ChallengeTest { @Override 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(); fail("Expected AcmeRetryAfterException"); } catch (AcmeRetryAfterException ex) { - assertThat(ex.getRetryAfter(), is(new Date(retryAfter))); + assertThat(ex.getRetryAfter(), is(retryAfter)); } assertThat(challenge.getStatus(), is(Status.VALID)); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java index 55b96279..2d42e9f0 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java @@ -25,9 +25,10 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.security.cert.X509Certificate; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -254,12 +255,12 @@ public class DefaultConnectionTest { */ @Test 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"; when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_ACCEPTED); 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)) { conn.conn = mockUrlConnection; @@ -297,7 +298,7 @@ public class DefaultConnectionTest { conn.handleRetryAfter(retryMsg); fail("no AcmeRetryAfterException was thrown"); } 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)); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededExceptionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededExceptionTest.java index 79beba36..8450d6cf 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededExceptionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRateLimitExceededExceptionTest.java @@ -17,9 +17,10 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; import java.net.URI; +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.Collection; -import java.util.Date; import org.junit.Test; @@ -35,7 +36,7 @@ public class AcmeRateLimitExceededExceptionTest { public void testAcmeRateLimitExceededException() { String type = "urn:ietf:params:acme:error:rateLimited"; String detail = "Too many requests per minute"; - Date retryAfter = new Date(System.currentTimeMillis() + 60 * 1000L); + Instant retryAfter = Instant.now().plus(Duration.ofMinutes(1)); Collection documents = Arrays.asList( URI.create("http://example.com/doc1.html"), URI.create("http://example.com/doc2.html")); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRetryAfterExceptionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRetryAfterExceptionTest.java index 8b047df2..dd020091 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRetryAfterExceptionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRetryAfterExceptionTest.java @@ -16,7 +16,8 @@ package org.shredzone.acme4j.exception; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -import java.util.Date; +import java.time.Duration; +import java.time.Instant; import org.junit.Test; @@ -31,16 +32,12 @@ public class AcmeRetryAfterExceptionTest { @Test public void testAcmeRetryAfterException() { String detail = "Too early"; - Date retryAfter = new Date(System.currentTimeMillis() + 60 * 1000L); + Instant retryAfter = Instant.now().plus(Duration.ofMinutes(1)); - AcmeRetryAfterException ex - = new AcmeRetryAfterException(detail, retryAfter); + AcmeRetryAfterException ex = new AcmeRetryAfterException(detail, retryAfter); assertThat(ex.getMessage(), is(detail)); 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 public void testNullAcmeRetryAfterException() { - Date retryAfter = new Date(System.currentTimeMillis() + 60 * 1000L); + Instant retryAfter = Instant.now().plus(Duration.ofMinutes(1)); - AcmeRetryAfterException ex - = new AcmeRetryAfterException(null, retryAfter); + AcmeRetryAfterException ex = new AcmeRetryAfterException(null, retryAfter); assertThat(ex.getMessage(), nullValue()); assertThat(ex.getRetryAfter(), is(retryAfter)); - - // make sure we get a copy of the Date object - assertThat(ex.getRetryAfter(), not(sameInstance(retryAfter))); } /** diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java index 112225b9..5452c590 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java @@ -21,11 +21,10 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.security.KeyPair; import java.security.Security; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import javax.xml.bind.DatatypeConverter; @@ -247,57 +246,55 @@ public class AcmeUtilsTest { /** * 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); } /** * Matches the given time and milliseconds. */ - private DateMatcher isDate(int year, int month, int dom, int hour, int minute, int second, int ms) { - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - cal.clear(); - cal.set(year, month - 1, dom, hour, minute, second); - cal.set(Calendar.MILLISECOND, ms); - return new DateMatcher(cal); + private InstantMatcher isDate(int year, int month, int dom, int hour, int minute, int second, int ms) { + Instant cmp = ZonedDateTime.of( + year, month, dom, hour, minute, second, ms * 1_000_000, + ZoneId.of("UTC")).toInstant(); + return new InstantMatcher(cmp); } /** * Date matcher that gives a readable output on mismatch. */ - private static class DateMatcher extends BaseMatcher { - private final Calendar cal; - private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); + private static class InstantMatcher extends BaseMatcher { + private final Instant cmp; + private final DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT; - public DateMatcher(Calendar cal) { - this.cal = cal; - sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + public InstantMatcher(Instant cmp) { + this.cmp = cmp; } @Override public boolean matches(Object item) { - if (!(item instanceof Date)) { + if (!(item instanceof Instant)) { return false; } - Date date = (Date) item; - return date.equals(cal.getTime()); + Instant date = (Instant) item; + return date.equals(cmp); } @Override public void describeTo(Description description) { - description.appendValue(sdf.format(cal.getTime())); + description.appendValue(dtf.format(cmp)); } @Override public void describeMismatch(Object item, Description description) { - if (!(item instanceof Date)) { - description.appendText("is not a Date"); + if (!(item instanceof Instant)) { + description.appendText("is not an Instant"); return; } - Date date = (Date) item; - description.appendText("was ").appendValue(sdf.format(date)); + Instant date = (Instant) item; + description.appendText("was ").appendValue(dtf.format(date)); } } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONBuilderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONBuilderTest.java index 4a4bbca9..0457338e 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONBuilderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONBuilderTest.java @@ -19,10 +19,10 @@ import static org.junit.Assert.assertThat; import java.io.IOException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; -import java.util.Calendar; -import java.util.Date; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Map; -import java.util.TimeZone; import org.jose4j.json.JsonUtil; import org.jose4j.lang.JoseException; @@ -81,13 +81,11 @@ public class JSONBuilderTest { */ @Test public void testDate() { - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+2")); - cal.clear(); - cal.set(2016, Calendar.JUNE, 1, 5, 13, 46); + Instant date = ZonedDateTime.of(2016, 6, 1, 5, 13, 46, 0, ZoneId.of("GMT+2")).toInstant(); JSONBuilder cb = new JSONBuilder(); - cb.put("fooDate", cal.getTime()); - cb.put("fooNull", (Date) null); + cb.put("fooDate", date); + cb.put("fooNull", (Instant) null); assertThat(cb.toString(), is("{\"fooDate\":\"2016-06-01T03:13:46Z\",\"fooNull\":null}")); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java index 518c8a54..697ea0b7 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java @@ -26,10 +26,11 @@ import java.io.ObjectOutputStream; import java.net.MalformedURLException; import java.net.URI; 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.NoSuchElementException; -import java.util.TimeZone; import org.junit.Test; import org.shredzone.acme4j.exception.AcmeProtocolException; @@ -145,9 +146,7 @@ public class JSONTest { */ @Test public void testGetter() throws MalformedURLException { - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - cal.clear(); - cal.set(2016, 0, 8); + Instant date = LocalDate.of(2016, 1, 8).atStartOfDay(ZoneId.of("UTC")).toInstant(); JSON json = TestUtils.getJsonAsObject("json"); @@ -155,7 +154,7 @@ public class JSONTest { assertThat(json.get("number").asInt(), is(123)); 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("date").asDate(), is(cal.getTime())); + assertThat(json.get("date").asInstant(), is(date)); JSON.Array array = json.get("array").asArray(); 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").asURI(), 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").asObject(), is(nullValue())); @@ -246,7 +245,7 @@ public class JSONTest { } try { - json.get("text").asDate(); + json.get("text").asInstant(); fail("no exception was thrown"); } catch (AcmeProtocolException ex) { // expected diff --git a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java index 94ecc550..c0169a3a 100644 --- a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java +++ b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java @@ -25,6 +25,8 @@ import java.security.KeyPair; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.time.Duration; +import java.time.Instant; import java.util.Date; import org.bouncycastle.asn1.x500.X500Name; @@ -212,17 +214,17 @@ public final class CertificateUtils { */ private static X509Certificate createCertificate(KeyPair keypair, String... subject) throws IOException { final long now = System.currentTimeMillis(); - final long validSpanMs = 7 * 24 * 60 * 60 * 1000L; final String signatureAlg = "SHA256withRSA"; try { X500Name issuer = new X500Name("CN=acme.invalid"); BigInteger serial = BigInteger.valueOf(now); - Date notBefore = new Date(now); - Date notAfter = new Date(now + validSpanMs); + Instant notBefore = Instant.ofEpochMilli(now); + Instant notAfter = notBefore.plus(Duration.ofDays(7)); 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]; for (int ix = 0; ix < subject.length; ix++) { diff --git a/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java b/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java index 0547712a..fb41e761 100644 --- a/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java +++ b/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java @@ -28,6 +28,8 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.time.Duration; +import java.time.Instant; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -132,13 +134,13 @@ public class CertificateUtilsTest { X509Certificate cert = CertificateUtils.createTlsSniCertificate(keypair, subject); - Date now = new Date(); - Date end = new Date(now.getTime() + (8 * 24 * 60 * 60 * 1000L)); + Instant now = Instant.now(); + Instant end = now.plus(Duration.ofDays(8)); assertThat(cert, not(nullValue())); - assertThat(cert.getNotAfter(), is(greaterThan(now))); - assertThat(cert.getNotAfter(), is(lessThan(end))); - assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(now))); + assertThat(cert.getNotAfter(), is(greaterThan(Date.from(now)))); + assertThat(cert.getNotAfter(), is(lessThan(Date.from(end)))); + assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(Date.from(now)))); assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid")); assertThat(getSANs(cert), containsInAnyOrder(subject)); } @@ -156,13 +158,13 @@ public class CertificateUtilsTest { X509Certificate cert = CertificateUtils.createTlsSni02Certificate(keypair, sanA, sanB); - Date now = new Date(); - Date end = new Date(now.getTime() + (8 * 24 * 60 * 60 * 1000L)); + Instant now = Instant.now(); + Instant end = now.plus(Duration.ofDays(8)); assertThat(cert, not(nullValue())); - assertThat(cert.getNotAfter(), is(greaterThan(now))); - assertThat(cert.getNotAfter(), is(lessThan(end))); - assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(now))); + assertThat(cert.getNotAfter(), is(greaterThan(Date.from(now)))); + assertThat(cert.getNotAfter(), is(lessThan(Date.from(end)))); + assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(Date.from(now)))); assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid")); assertThat(getSANs(cert), containsInAnyOrder(sanA, sanB)); }