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 Date expires;
|
||||||
private List<Challenge> challenges;
|
private List<Challenge> challenges;
|
||||||
private List<List<Challenge>> combinations;
|
private List<List<Challenge>> combinations;
|
||||||
|
private boolean loaded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link Authorization} and binds it to the {@link Session}.
|
* 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.
|
* Gets the domain name to be authorized.
|
||||||
*/
|
*/
|
||||||
public String getDomain() {
|
public String getDomain() {
|
||||||
|
load();
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +79,7 @@ public class Authorization extends AcmeResource {
|
||||||
* Gets the authorization status.
|
* Gets the authorization status.
|
||||||
*/
|
*/
|
||||||
public Status getStatus() {
|
public Status getStatus() {
|
||||||
|
load();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +87,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 Date getExpires() {
|
||||||
|
load();
|
||||||
return expires;
|
return expires;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +95,7 @@ public class Authorization extends AcmeResource {
|
||||||
* Gets a list of all challenges offered by the server.
|
* Gets a list of all challenges offered by the server.
|
||||||
*/
|
*/
|
||||||
public List<Challenge> getChallenges() {
|
public List<Challenge> getChallenges() {
|
||||||
|
load();
|
||||||
return challenges;
|
return challenges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +103,7 @@ public class Authorization extends AcmeResource {
|
||||||
* Gets all combinations of challenges supported by the server.
|
* Gets all combinations of challenges supported by the server.
|
||||||
*/
|
*/
|
||||||
public List<List<Challenge>> getCombinations() {
|
public List<List<Challenge>> getCombinations() {
|
||||||
|
load();
|
||||||
return combinations;
|
return combinations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +142,7 @@ public class Authorization extends AcmeResource {
|
||||||
* validation.
|
* validation.
|
||||||
*/
|
*/
|
||||||
public Collection<Challenge> findCombination(String... types) {
|
public Collection<Challenge> findCombination(String... types) {
|
||||||
if (combinations == null) {
|
if (getCombinations() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +151,7 @@ public class Authorization extends AcmeResource {
|
||||||
|
|
||||||
Collection<Challenge> result = null;
|
Collection<Challenge> result = null;
|
||||||
|
|
||||||
for (List<Challenge> combination : combinations) {
|
for (List<Challenge> combination : getCombinations()) {
|
||||||
combinationTypes.clear();
|
combinationTypes.clear();
|
||||||
for (Challenge c : combination) {
|
for (Challenge c : combination) {
|
||||||
combinationTypes.add(c.getType());
|
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.
|
* Sets the properties according to the given JSON data.
|
||||||
*
|
*
|
||||||
|
@ -264,6 +285,8 @@ public class Authorization extends AcmeResource {
|
||||||
cmb.add(cr);
|
cmb.add(cr);
|
||||||
this.combinations = cmb;
|
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.AcmeException;
|
||||||
import org.shredzone.acme4j.exception.AcmeNetworkException;
|
import org.shredzone.acme4j.exception.AcmeNetworkException;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
|
||||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||||
import org.shredzone.acme4j.util.SignatureUtils;
|
import org.shredzone.acme4j.util.SignatureUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -55,6 +56,7 @@ public class Registration extends AcmeResource {
|
||||||
private URI authorizations;
|
private URI authorizations;
|
||||||
private URI certificates;
|
private URI certificates;
|
||||||
private Status status;
|
private Status status;
|
||||||
|
private boolean loaded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of {@link Registration} and binds it to the {@link Session}.
|
* 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.
|
* Returns the URI of the agreement document the user is required to accept.
|
||||||
*/
|
*/
|
||||||
public URI getAgreement() {
|
public URI getAgreement() {
|
||||||
|
if (agreement == null) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
return agreement;
|
return agreement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +95,7 @@ public class Registration extends AcmeResource {
|
||||||
* List of contact addresses (emails, phone numbers etc).
|
* List of contact addresses (emails, phone numbers etc).
|
||||||
*/
|
*/
|
||||||
public List<URI> getContacts() {
|
public List<URI> getContacts() {
|
||||||
|
load();
|
||||||
return Collections.unmodifiableList(contacts);
|
return Collections.unmodifiableList(contacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +103,7 @@ public class Registration extends AcmeResource {
|
||||||
* Returns the current status of the registration.
|
* Returns the current status of the registration.
|
||||||
*/
|
*/
|
||||||
public Status getStatus() {
|
public Status getStatus() {
|
||||||
|
load();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +120,7 @@ public class Registration extends AcmeResource {
|
||||||
*/
|
*/
|
||||||
public Iterator<Authorization> getAuthorizations() throws AcmeException {
|
public Iterator<Authorization> getAuthorizations() throws AcmeException {
|
||||||
LOG.debug("getAuthorizations");
|
LOG.debug("getAuthorizations");
|
||||||
|
load();
|
||||||
return new ResourceIterator<Authorization>(getSession(), "authorizations", authorizations) {
|
return new ResourceIterator<Authorization>(getSession(), "authorizations", authorizations) {
|
||||||
@Override
|
@Override
|
||||||
protected Authorization create(Session session, URI uri) {
|
protected Authorization create(Session session, URI uri) {
|
||||||
|
@ -134,6 +142,7 @@ public class Registration extends AcmeResource {
|
||||||
*/
|
*/
|
||||||
public Iterator<Certificate> getCertificates() throws AcmeException {
|
public Iterator<Certificate> getCertificates() throws AcmeException {
|
||||||
LOG.debug("getCertificates");
|
LOG.debug("getCertificates");
|
||||||
|
load();
|
||||||
return new ResourceIterator<Certificate>(getSession(), "certificates", certificates) {
|
return new ResourceIterator<Certificate>(getSession(), "certificates", certificates) {
|
||||||
@Override
|
@Override
|
||||||
protected Certificate create(Session session, URI uri) {
|
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.
|
* Sets registration properties according to the given JSON data.
|
||||||
*
|
*
|
||||||
|
@ -373,6 +397,8 @@ public class Registration extends AcmeResource {
|
||||||
if (tos != null) {
|
if (tos != null) {
|
||||||
this.agreement = tos;
|
this.agreement = tos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.net.URI;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.shredzone.acme4j.challenge.Challenge;
|
import org.shredzone.acme4j.challenge.Challenge;
|
||||||
|
@ -158,6 +159,49 @@ public class AuthorizationTest {
|
||||||
provider.close();
|
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.
|
* 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.io.IOException;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.jose4j.jws.JsonWebSignature;
|
import org.jose4j.jws.JsonWebSignature;
|
||||||
import org.jose4j.lang.JoseException;
|
import org.jose4j.lang.JoseException;
|
||||||
|
@ -55,25 +55,6 @@ public class RegistrationTest {
|
||||||
private URI agreementUri = URI.create("http://example.com/agreement.pdf");
|
private URI agreementUri = URI.create("http://example.com/agreement.pdf");
|
||||||
private URI chainUri = URI.create("http://example.com/acme/chain");
|
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.
|
* Test that a registration can be updated.
|
||||||
*/
|
*/
|
||||||
|
@ -154,6 +135,56 @@ public class RegistrationTest {
|
||||||
provider.close();
|
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.
|
* Test that a new {@link Authorization} can be created.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue