Refactor, make AbstractAcmeClientProvider easier to use (and test)

pull/17/merge
Richard Körber 2015-12-13 16:01:25 +01:00
parent f88bdf7e5f
commit 62ed304f3a
3 changed files with 66 additions and 49 deletions

View File

@ -16,19 +16,26 @@ package org.shredzone.acme4j.provider;
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.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.shredzone.acme4j.AcmeClient;
import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.DnsChallenge; import org.shredzone.acme4j.challenge.DnsChallenge;
import org.shredzone.acme4j.challenge.GenericChallenge; import org.shredzone.acme4j.challenge.GenericChallenge;
import org.shredzone.acme4j.challenge.HttpChallenge; import org.shredzone.acme4j.challenge.HttpChallenge;
import org.shredzone.acme4j.challenge.ProofOfPossessionChallenge; import org.shredzone.acme4j.challenge.ProofOfPossessionChallenge;
import org.shredzone.acme4j.challenge.TlsSniChallenge; import org.shredzone.acme4j.challenge.TlsSniChallenge;
import org.shredzone.acme4j.impl.GenericAcmeClient;
/** /**
* Abstract implementation of {@link AcmeClientProvider}. It consists of a challenge * Abstract implementation of {@link AcmeClientProvider}. It consists of a challenge
* registry and a standard {@link #openConnection(URI)} implementation. * registry and a standard {@link #openConnection(URI)} implementation.
* <p>
* Implementing classes must implement at least {@link AcmeClientProvider#accepts(URI)}
* and {@link AbstractAcmeClientProvider#resolve(URI)}.
* *
* @author Richard "Shred" Körber * @author Richard "Shred" Körber
*/ */
@ -42,6 +49,26 @@ public abstract class AbstractAcmeClientProvider implements AcmeClientProvider {
registerBaseChallenges(); registerBaseChallenges();
} }
/**
* Resolves the server URI and returns the matching directory URI.
*
* @param serverUri
* Server {@link URI}
* @return Resolved directory {@link URI}
* @throws IllegalArgumentException
* if the server {@link URI} is not accepted
*/
protected abstract URI resolve(URI serverUri);
@Override
public AcmeClient connect(URI serverUri) {
if (!accepts(serverUri)) {
throw new IllegalArgumentException("This provider does not accept " + serverUri);
}
return createAcmeClient(resolve(serverUri));
}
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends Challenge> T createChallenge(String type) { public <T extends Challenge> T createChallenge(String type) {
@ -79,9 +106,26 @@ public abstract class AbstractAcmeClientProvider implements AcmeClientProvider {
* constructor. * constructor.
*/ */
protected void registerChallenge(String type, Class<? extends Challenge> clazz) { protected void registerChallenge(String type, Class<? extends Challenge> clazz) {
if (type == null) {
throw new NullPointerException("type must not be null");
}
if (clazz == null) {
throw new NullPointerException("Challenge class must not be null");
}
if (type.trim().isEmpty()) {
throw new IllegalArgumentException("type must not be empty");
}
challenges.put(type, clazz); challenges.put(type, clazz);
} }
/**
* Returns all registered challenge types.
*/
protected Collection<String> getRegisteredChallengeTypes() {
return Collections.unmodifiableCollection(challenges.keySet());
}
/** /**
* Registers all standard challenges as specified in the ACME specifications. * Registers all standard challenges as specified in the ACME specifications.
* <p> * <p>
@ -95,4 +139,15 @@ public abstract class AbstractAcmeClientProvider implements AcmeClientProvider {
registerChallenge(HttpChallenge.TYPE, HttpChallenge.class); registerChallenge(HttpChallenge.TYPE, HttpChallenge.class);
} }
/**
* Creates an {@link AcmeClient} for the given directory URI.
*
* @param directoryUri
* Directory {@link URI}
* @return {@link AcmeClient}
*/
protected AcmeClient createAcmeClient(URI directoryUri) {
return new GenericAcmeClient(this, directoryUri);
}
} }

View File

@ -15,9 +15,6 @@ package org.shredzone.acme4j.provider;
import java.net.URI; import java.net.URI;
import org.shredzone.acme4j.AcmeClient;
import org.shredzone.acme4j.impl.GenericAcmeClient;
/** /**
* A generic {@link AcmeClientProvider}. It should be working for all ACME servers * A generic {@link AcmeClientProvider}. It should be working for all ACME servers
* complying to the ACME specifications. * complying to the ACME specifications.
@ -35,12 +32,8 @@ public class GenericAcmeClientProvider extends AbstractAcmeClientProvider {
} }
@Override @Override
public AcmeClient connect(URI serverUri) { protected URI resolve(URI serverUri) {
if (!accepts(serverUri)) { return serverUri;
throw new IllegalArgumentException("This provider does not accept " + serverUri);
}
return new GenericAcmeClient(this, serverUri);
} }
} }

View File

@ -28,9 +28,6 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import org.shredzone.acme4j.AcmeClient;
import org.shredzone.acme4j.impl.GenericAcmeClient;
/** /**
* An {@link AcmeClientProvider} for <em>Let's Encrypt</em>. * An {@link AcmeClientProvider} for <em>Let's Encrypt</em>.
* <p> * <p>
@ -57,44 +54,7 @@ public class LetsEncryptAcmeClientProvider extends AbstractAcmeClientProvider {
} }
@Override @Override
public AcmeClient connect(URI serverUri) {
return createAcmeClient(resolve(serverUri));
}
@Override
public HttpURLConnection openConnection(URI uri) throws IOException {
HttpURLConnection conn = super.openConnection(uri);
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection) conn).setSSLSocketFactory(createSocketFactory());
}
return conn;
}
/**
* Creates an {@link AcmeClient} for the given directory URI.
*
* @param directoryUri
* Directory {@link URI}
* @return {@link AcmeClient}
*/
protected AcmeClient createAcmeClient(URI directoryUri) {
return new GenericAcmeClient(this, directoryUri);
}
/**
* Resolves the server URI and returns the matching directory URI.
*
* @param serverUri
* Server {@link URI} to resolve
* @return Directory {@link URI}
* @throws IllegalArgumentException
* if the server URI cannot be resolved
*/
protected URI resolve(URI serverUri) { protected URI resolve(URI serverUri) {
if (!accepts(serverUri)) {
throw new IllegalArgumentException("Unknown URI " + serverUri);
}
String path = serverUri.getPath(); String path = serverUri.getPath();
String directoryUri; String directoryUri;
if (path == null || "".equals(path) || "/".equals(path) || "/v01".equals(path)) { if (path == null || "".equals(path) || "/".equals(path) || "/v01".equals(path)) {
@ -112,6 +72,15 @@ public class LetsEncryptAcmeClientProvider extends AbstractAcmeClientProvider {
} }
} }
@Override
public HttpURLConnection openConnection(URI uri) throws IOException {
HttpURLConnection conn = super.openConnection(uri);
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection) conn).setSSLSocketFactory(createSocketFactory());
}
return conn;
}
/** /**
* Lazily creates an {@link SSLSocketFactory} that exclusively accepts the Let's * Lazily creates an {@link SSLSocketFactory} that exclusively accepts the Let's
* Encrypt certificate. * Encrypt certificate.