mirror of https://github.com/shred/acme4j
Lazily load current status of Authorization and Registration.
parent
5049cd5ffd
commit
9b86b88e4a
|
@ -48,6 +48,7 @@ public class Authorization extends AcmeResource {
|
|||
private Date expires;
|
||||
private List<Challenge> challenges;
|
||||
private List<List<Challenge>> combinations;
|
||||
private boolean loaded = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Authorization} and binds it to the {@link Session}.
|
||||
|
@ -70,6 +71,7 @@ public class Authorization extends AcmeResource {
|
|||
* Gets the domain name to be authorized.
|
||||
*/
|
||||
public String getDomain() {
|
||||
load();
|
||||
return domain;
|
||||
}
|
||||
|
||||
|
@ -77,6 +79,7 @@ public class Authorization extends AcmeResource {
|
|||
* Gets the authorization status.
|
||||
*/
|
||||
public Status getStatus() {
|
||||
load();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -84,6 +87,7 @@ public class Authorization extends AcmeResource {
|
|||
* Gets the expiry date of the authorization, if set by the server.
|
||||
*/
|
||||
public Date getExpires() {
|
||||
load();
|
||||
return expires;
|
||||
}
|
||||
|
||||
|
@ -91,6 +95,7 @@ public class Authorization extends AcmeResource {
|
|||
* Gets a list of all challenges offered by the server.
|
||||
*/
|
||||
public List<Challenge> getChallenges() {
|
||||
load();
|
||||
return challenges;
|
||||
}
|
||||
|
||||
|
@ -98,6 +103,7 @@ public class Authorization extends AcmeResource {
|
|||
* Gets all combinations of challenges supported by the server.
|
||||
*/
|
||||
public List<List<Challenge>> getCombinations() {
|
||||
load();
|
||||
return combinations;
|
||||
}
|
||||
|
||||
|
@ -136,7 +142,7 @@ public class Authorization extends AcmeResource {
|
|||
* validation.
|
||||
*/
|
||||
public Collection<Challenge> findCombination(String... types) {
|
||||
if (combinations == null) {
|
||||
if (getCombinations() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -145,7 +151,7 @@ public class Authorization extends AcmeResource {
|
|||
|
||||
Collection<Challenge> result = null;
|
||||
|
||||
for (List<Challenge> combination : combinations) {
|
||||
for (List<Challenge> combination : getCombinations()) {
|
||||
combinationTypes.clear();
|
||||
for (Challenge c : combination) {
|
||||
combinationTypes.add(c.getType());
|
||||
|
@ -212,6 +218,21 @@ public class Authorization extends AcmeResource {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily updates the object's state when one of the getters is invoked.
|
||||
*/
|
||||
protected void load() {
|
||||
if (!loaded) {
|
||||
try {
|
||||
update();
|
||||
} catch (AcmeRetryAfterException ex) {
|
||||
// ignore... The object was still updated.
|
||||
} catch (AcmeException ex) {
|
||||
throw new AcmeProtocolException("Could not load lazily", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the properties according to the given JSON data.
|
||||
*
|
||||
|
@ -264,6 +285,8 @@ public class Authorization extends AcmeResource {
|
|||
cmb.add(cr);
|
||||
this.combinations = cmb;
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.shredzone.acme4j.connector.ResourceIterator;
|
|||
import org.shredzone.acme4j.exception.AcmeException;
|
||||
import org.shredzone.acme4j.exception.AcmeNetworkException;
|
||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
|
||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||
import org.shredzone.acme4j.util.SignatureUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -55,6 +56,7 @@ public class Registration extends AcmeResource {
|
|||
private URI authorizations;
|
||||
private URI certificates;
|
||||
private Status status;
|
||||
private boolean loaded = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Registration} and binds it to the {@link Session}.
|
||||
|
@ -83,6 +85,9 @@ public class Registration extends AcmeResource {
|
|||
* Returns the URI of the agreement document the user is required to accept.
|
||||
*/
|
||||
public URI getAgreement() {
|
||||
if (agreement == null) {
|
||||
load();
|
||||
}
|
||||
return agreement;
|
||||
}
|
||||
|
||||
|
@ -90,6 +95,7 @@ public class Registration extends AcmeResource {
|
|||
* List of contact addresses (emails, phone numbers etc).
|
||||
*/
|
||||
public List<URI> getContacts() {
|
||||
load();
|
||||
return Collections.unmodifiableList(contacts);
|
||||
}
|
||||
|
||||
|
@ -97,6 +103,7 @@ public class Registration extends AcmeResource {
|
|||
* Returns the current status of the registration.
|
||||
*/
|
||||
public Status getStatus() {
|
||||
load();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -113,6 +120,7 @@ public class Registration extends AcmeResource {
|
|||
*/
|
||||
public Iterator<Authorization> getAuthorizations() throws AcmeException {
|
||||
LOG.debug("getAuthorizations");
|
||||
load();
|
||||
return new ResourceIterator<Authorization>(getSession(), "authorizations", authorizations) {
|
||||
@Override
|
||||
protected Authorization create(Session session, URI uri) {
|
||||
|
@ -134,6 +142,7 @@ public class Registration extends AcmeResource {
|
|||
*/
|
||||
public Iterator<Certificate> getCertificates() throws AcmeException {
|
||||
LOG.debug("getCertificates");
|
||||
load();
|
||||
return new ResourceIterator<Certificate>(getSession(), "certificates", certificates) {
|
||||
@Override
|
||||
protected Certificate create(Session session, URI uri) {
|
||||
|
@ -311,6 +320,21 @@ public class Registration extends AcmeResource {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily updates the object's state when one of the getters is invoked.
|
||||
*/
|
||||
protected void load() {
|
||||
if (!loaded) {
|
||||
try {
|
||||
update();
|
||||
} catch (AcmeRetryAfterException ex) {
|
||||
// ignore... The object was still updated.
|
||||
} catch (AcmeException ex) {
|
||||
throw new AcmeProtocolException("Could not load lazily", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets registration properties according to the given JSON data.
|
||||
*
|
||||
|
@ -373,6 +397,8 @@ public class Registration extends AcmeResource {
|
|||
if (tos != null) {
|
||||
this.agreement = tos;
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.net.URI;
|
|||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.shredzone.acme4j.challenge.Challenge;
|
||||
|
@ -158,6 +159,49 @@ public class AuthorizationTest {
|
|||
provider.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test lazy loading.
|
||||
*/
|
||||
@Test
|
||||
public void testLazyLoading() throws Exception {
|
||||
final AtomicBoolean requestWasSent = new AtomicBoolean(false);
|
||||
|
||||
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
||||
@Override
|
||||
public int sendRequest(URI uri) {
|
||||
requestWasSent.set(true);
|
||||
assertThat(uri, is(locationUri));
|
||||
return HttpURLConnection.HTTP_OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> readJsonResponse() {
|
||||
return getJsonAsMap("updateAuthorizationResponse");
|
||||
}
|
||||
};
|
||||
|
||||
Session session = provider.createSession();
|
||||
|
||||
provider.putTestChallenge("http-01", new Http01Challenge(session));
|
||||
provider.putTestChallenge("dns-01", new Dns01Challenge(session));
|
||||
|
||||
Authorization auth = new Authorization(session, locationUri);
|
||||
|
||||
// Lazy loading
|
||||
assertThat(requestWasSent.get(), is(false));
|
||||
assertThat(auth.getDomain(), is("example.org"));
|
||||
assertThat(requestWasSent.get(), is(true));
|
||||
|
||||
// Subsequent queries do not trigger another load
|
||||
requestWasSent.set(false);
|
||||
assertThat(auth.getDomain(), is("example.org"));
|
||||
assertThat(auth.getStatus(), is(Status.VALID));
|
||||
assertThat(auth.getExpires(), is(TimestampParser.parse("2016-01-02T17:12:40Z")));
|
||||
assertThat(requestWasSent.get(), is(false));
|
||||
|
||||
provider.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that authorization is properly updated, with retry-after header set.
|
||||
*/
|
||||
|
|
|
@ -21,13 +21,13 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
|
|||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.jose4j.jws.JsonWebSignature;
|
||||
import org.jose4j.lang.JoseException;
|
||||
|
@ -55,25 +55,6 @@ public class RegistrationTest {
|
|||
private URI agreementUri = URI.create("http://example.com/agreement.pdf");
|
||||
private URI chainUri = URI.create("http://example.com/acme/chain");
|
||||
|
||||
/**
|
||||
* Test getters. Make sure object cannot be modified.
|
||||
*/
|
||||
@Test
|
||||
public void testGetters() throws IOException, URISyntaxException {
|
||||
Session session = TestUtils.session();
|
||||
Registration registration = new Registration(session, locationUri);
|
||||
|
||||
assertThat(registration.getAgreement(), is(nullValue()));
|
||||
assertThat(registration.getContacts(), is(empty()));
|
||||
|
||||
try {
|
||||
registration.getContacts().add(new URI("mailto:foo@example.com"));
|
||||
fail("could modify contacts list");
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a registration can be updated.
|
||||
*/
|
||||
|
@ -154,6 +135,56 @@ public class RegistrationTest {
|
|||
provider.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test lazy loading.
|
||||
*/
|
||||
@Test
|
||||
public void testLazyLoading() throws AcmeException, IOException {
|
||||
final AtomicBoolean requestWasSent = new AtomicBoolean(false);
|
||||
|
||||
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
||||
@Override
|
||||
public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session) {
|
||||
requestWasSent.set(true);
|
||||
assertThat(uri, is(locationUri));
|
||||
return HttpURLConnection.HTTP_ACCEPTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> readJsonResponse() {
|
||||
return getJsonAsMap("updateRegistrationResponse");
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getLocation() {
|
||||
return locationUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getLink(String relation) {
|
||||
switch(relation) {
|
||||
case "terms-of-service": return agreementUri;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Registration registration = new Registration(provider.createSession(), locationUri);
|
||||
|
||||
// Lazy loading
|
||||
assertThat(requestWasSent.get(), is(false));
|
||||
assertThat(registration.getAgreement(), is(agreementUri));
|
||||
assertThat(requestWasSent.get(), is(true));
|
||||
|
||||
// Subsequent queries do not trigger another load
|
||||
requestWasSent.set(false);
|
||||
assertThat(registration.getAgreement(), is(agreementUri));
|
||||
assertThat(registration.getStatus(), is(Status.GOOD));
|
||||
assertThat(requestWasSent.get(), is(false));
|
||||
|
||||
provider.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a new {@link Authorization} can be created.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue