Add module-info definitions, fix related issues

pull/91/head
Richard Körber 2020-06-02 13:40:36 +02:00
parent 8de55cdd9b
commit 610b9366bd
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
13 changed files with 182 additions and 132 deletions

View File

@ -15,7 +15,7 @@ It is an independent open source implementation that is not affiliated with or e
* Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation * Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation
* Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental) * Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental)
* Easy to use Java API * Easy to use Java API
* Requires JRE 8 (update 101) or higher * Requires JRE 8 (update 101) or higher. For building the project, Java 9 or higher is required.
* Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22) * Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22)
* Requires [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home), [Bouncy Castle](https://www.bouncycastle.org/) and [slf4j](http://www.slf4j.org/) as dependencies. If you have other means of generating key pairs and CSRs, you can even do without `acme4j-utils` and Bouncy Castle as dependency. * Requires [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home), [Bouncy Castle](https://www.bouncycastle.org/) and [slf4j](http://www.slf4j.org/) as dependencies. If you have other means of generating key pairs and CSRs, you can even do without `acme4j-utils` and Bouncy Castle as dependency.
* Extensive unit and integration tests * Extensive unit and integration tests
@ -23,7 +23,7 @@ It is an independent open source implementation that is not affiliated with or e
## Usage ## Usage
* See the [online documentation](https://shredzone.org/maven/acme4j/) about how to use _acme4j_. * See the [online documentation](https://shredzone.org/maven/acme4j/) about how to use _acme4j_.
* For a quick start, have a look at [the source code of an example](https://github.com/shred/acme4j/blob/master/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java). * For a quick start, have a look at [the source code of an example](https://github.com/shred/acme4j/blob/master/acme4j-example/src/main/java/org/shredzone/acme4j/example/ClientTest.java).
## Contribute ## Contribute

View File

@ -41,13 +41,11 @@
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<archive> <!-- Required because unit tests provides an own AcmeProvider
<manifestEntries> which cannot be added to the module-info definition. -->
<Automatic-Module-Name>org.shredzone.acme4j</Automatic-Module-Name> <useModulePath>false</useModulePath>
</manifestEntries>
</archive>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View File

@ -0,0 +1,33 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2020 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
module org.shredzone.acme4j {
requires static com.github.spotbugs.annotations;
requires org.jose4j;
requires org.slf4j;
exports org.shredzone.acme4j;
exports org.shredzone.acme4j.challenge;
exports org.shredzone.acme4j.connector;
exports org.shredzone.acme4j.exception;
exports org.shredzone.acme4j.provider;
exports org.shredzone.acme4j.toolbox;
uses org.shredzone.acme4j.provider.AcmeProvider;
provides org.shredzone.acme4j.provider.AcmeProvider
with org.shredzone.acme4j.provider.GenericAcmeProvider,
org.shredzone.acme4j.provider.letsencrypt.LetsEncryptAcmeProvider,
org.shredzone.acme4j.provider.pebble.PebbleAcmeProvider;
}

View File

@ -35,17 +35,6 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>org.shredzone.acme4j.example</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId> <artifactId>exec-maven-plugin</artifactId>
@ -58,7 +47,7 @@
</execution> </execution>
</executions> </executions>
<configuration> <configuration>
<mainClass>org.shredzone.acme4j.ClientTest</mainClass> <mainClass>org.shredzone.acme4j.example.ClientTest</mainClass>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View File

@ -0,0 +1,23 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2020 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
module org.shredzone.acme4j.example {
requires org.shredzone.acme4j;
requires org.shredzone.acme4j.utils;
requires java.desktop;
requires org.bouncycastle.provider;
requires org.slf4j;
}

View File

@ -11,7 +11,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/ */
package org.shredzone.acme4j; package org.shredzone.acme4j.example;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
@ -28,6 +28,13 @@ import java.util.Collection;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.shredzone.acme4j.Account;
import org.shredzone.acme4j.AccountBuilder;
import org.shredzone.acme4j.Authorization;
import org.shredzone.acme4j.Certificate;
import org.shredzone.acme4j.Order;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge; import org.shredzone.acme4j.challenge.Dns01Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge; import org.shredzone.acme4j.challenge.Http01Challenge;
@ -63,14 +70,14 @@ public class ClientTest {
private static final Logger LOG = LoggerFactory.getLogger(ClientTest.class); private static final Logger LOG = LoggerFactory.getLogger(ClientTest.class);
private enum ChallengeType { HTTP, DNS } private enum ChallengeType {HTTP, DNS}
/** /**
* Generates a certificate for the given domains. Also takes care for the registration * Generates a certificate for the given domains. Also takes care for the registration
* process. * process.
* *
* @param domains * @param domains
* Domains to get a common certificate for * Domains to get a common certificate for
*/ */
public void fetchCertificate(Collection<String> domains) throws IOException, AcmeException { public void fetchCertificate(Collection<String> domains) throws IOException, AcmeException {
// Load the user key file. If there is no key file, create a new one. // Load the user key file. If there is no key file, create a new one.
@ -144,8 +151,8 @@ public class ClientTest {
} }
/** /**
* Loads a user key pair from {@value #USER_KEY_FILE}. If the file does not exist, * Loads a user key pair from {@link #USER_KEY_FILE}. If the file does not exist, a
* a new key pair is generated and saved. * new key pair is generated and saved.
* <p> * <p>
* Keep this key pair in a safe place! In a production environment, you will not be * Keep this key pair in a safe place! In a production environment, you will not be
* able to access your account again if you should lose the key pair. * able to access your account again if you should lose the key pair.
@ -170,7 +177,7 @@ public class ClientTest {
} }
/** /**
* Loads a domain key pair from {@value #DOMAIN_KEY_FILE}. If the file does not exist, * Loads a domain key pair from {@link #DOMAIN_KEY_FILE}. If the file does not exist,
* a new key pair is generated and saved. * a new key pair is generated and saved.
* *
* @return Domain {@link KeyPair}. * @return Domain {@link KeyPair}.
@ -196,12 +203,12 @@ public class ClientTest {
* <p> * <p>
* This is a simple way of finding your {@link Account}. A better way is to get the * This is a simple way of finding your {@link Account}. A better way is to get the
* URL of your new account with {@link Account#getLocation()} and store it somewhere. * URL of your new account with {@link Account#getLocation()} and store it somewhere.
* If you need to get access to your account later, reconnect to it via * If you need to get access to your account later, reconnect to it via {@link
* {@link Session#login(URL, KeyPair)} by using the stored location. * Session#login(URL, KeyPair)} by using the stored location.
* *
* @param session * @param session
* {@link Session} to bind with * {@link Session} to bind with
* @return {@link Login} that is connected to your account * @return {@link Account}
*/ */
private Account findOrRegisterAccount(Session session, KeyPair accountKey) throws AcmeException { private Account findOrRegisterAccount(Session session, KeyPair accountKey) throws AcmeException {
// Ask the user to accept the TOS, if server provides us with a link. // Ask the user to accept the TOS, if server provides us with a link.
@ -211,9 +218,9 @@ public class ClientTest {
} }
Account account = new AccountBuilder() Account account = new AccountBuilder()
.agreeToTermsOfService() .agreeToTermsOfService()
.useKeyPair(accountKey) .useKeyPair(accountKey)
.create(session); .create(session);
LOG.info("Registered a new user, URL: {}", account.getLocation()); LOG.info("Registered a new user, URL: {}", account.getLocation());
return account; return account;
@ -224,7 +231,7 @@ public class ClientTest {
* retrieve a signed certificate for the domain later. * retrieve a signed certificate for the domain later.
* *
* @param auth * @param auth
* {@link Authorization} to perform * {@link Authorization} to perform
*/ */
private void authorize(Authorization auth) throws AcmeException { private void authorize(Authorization auth) throws AcmeException {
LOG.info("Authorization for domain {}", auth.getIdentifier().getDomain()); LOG.info("Authorization for domain {}", auth.getIdentifier().getDomain());
@ -299,7 +306,7 @@ public class ClientTest {
* use a servlet that returns {@link Http01Challenge#getAuthorization()}. * use a servlet that returns {@link Http01Challenge#getAuthorization()}.
* *
* @param auth * @param auth
* {@link Authorization} to find the challenge in * {@link Authorization} to find the challenge in
* @return {@link Challenge} to verify * @return {@link Challenge} to verify
*/ */
public Challenge httpChallenge(Authorization auth) throws AcmeException { public Challenge httpChallenge(Authorization auth) throws AcmeException {
@ -312,7 +319,7 @@ public class ClientTest {
// Output the challenge, wait for acknowledge... // Output the challenge, wait for acknowledge...
LOG.info("Please create a file in your web server's base directory."); LOG.info("Please create a file in your web server's base directory.");
LOG.info("It must be reachable at: http://{}/.well-known/acme-challenge/{}", LOG.info("It must be reachable at: http://{}/.well-known/acme-challenge/{}",
auth.getIdentifier().getDomain(), challenge.getToken()); auth.getIdentifier().getDomain(), challenge.getToken());
LOG.info("File name: {}", challenge.getToken()); LOG.info("File name: {}", challenge.getToken());
LOG.info("Content: {}", challenge.getAuthorization()); LOG.info("Content: {}", challenge.getAuthorization());
LOG.info("The file must not contain any leading or trailing whitespaces or line breaks!"); LOG.info("The file must not contain any leading or trailing whitespaces or line breaks!");
@ -321,10 +328,10 @@ public class ClientTest {
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
message.append("Please create a file in your web server's base directory.\n\n"); message.append("Please create a file in your web server's base directory.\n\n");
message.append("http://") message.append("http://")
.append(auth.getIdentifier().getDomain()) .append(auth.getIdentifier().getDomain())
.append("/.well-known/acme-challenge/") .append("/.well-known/acme-challenge/")
.append(challenge.getToken()) .append(challenge.getToken())
.append("\n\n"); .append("\n\n");
message.append("Content:\n\n"); message.append("Content:\n\n");
message.append(challenge.getAuthorization()); message.append(challenge.getAuthorization());
acceptChallenge(message.toString()); acceptChallenge(message.toString());
@ -341,7 +348,7 @@ public class ClientTest {
* production environment, you would rather configure your DNS automatically. * production environment, you would rather configure your DNS automatically.
* *
* @param auth * @param auth
* {@link Authorization} to find the challenge in * {@link Authorization} to find the challenge in
* @return {@link Challenge} to verify * @return {@link Challenge} to verify
*/ */
public Challenge dnsChallenge(Authorization auth) throws AcmeException { public Challenge dnsChallenge(Authorization auth) throws AcmeException {
@ -354,15 +361,15 @@ public class ClientTest {
// Output the challenge, wait for acknowledge... // Output the challenge, wait for acknowledge...
LOG.info("Please create a TXT record:"); LOG.info("Please create a TXT record:");
LOG.info("_acme-challenge.{}. IN TXT {}", LOG.info("_acme-challenge.{}. IN TXT {}",
auth.getIdentifier().getDomain(), challenge.getDigest()); auth.getIdentifier().getDomain(), challenge.getDigest());
LOG.info("If you're ready, dismiss the dialog..."); LOG.info("If you're ready, dismiss the dialog...");
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
message.append("Please create a TXT record:\n\n"); message.append("Please create a TXT record:\n\n");
message.append("_acme-challenge.") message.append("_acme-challenge.")
.append(auth.getIdentifier().getDomain()) .append(auth.getIdentifier().getDomain())
.append(". IN TXT ") .append(". IN TXT ")
.append(challenge.getDigest()); .append(challenge.getDigest());
acceptChallenge(message.toString()); acceptChallenge(message.toString());
return challenge; return challenge;
@ -373,13 +380,13 @@ public class ClientTest {
* dismissal. If the user cancelled the dialog, an exception is thrown. * dismissal. If the user cancelled the dialog, an exception is thrown.
* *
* @param message * @param message
* Instructions to be shown in the dialog * Instructions to be shown in the dialog
*/ */
public void acceptChallenge(String message) throws AcmeException { public void acceptChallenge(String message) throws AcmeException {
int option = JOptionPane.showConfirmDialog(null, int option = JOptionPane.showConfirmDialog(null,
message, message,
"Prepare Challenge", "Prepare Challenge",
JOptionPane.OK_CANCEL_OPTION); JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) { if (option == JOptionPane.CANCEL_OPTION) {
throw new AcmeException("User cancelled the challenge"); throw new AcmeException("User cancelled the challenge");
} }
@ -390,13 +397,13 @@ public class ClientTest {
* dismissal. * dismissal.
* *
* @param message * @param message
* Instructions to be shown in the dialog * Instructions to be shown in the dialog
*/ */
public void completeChallenge(String message) throws AcmeException { public void completeChallenge(String message) throws AcmeException {
JOptionPane.showMessageDialog(null, JOptionPane.showMessageDialog(null,
message, message,
"Complete Challenge", "Complete Challenge",
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
} }
/** /**
@ -404,13 +411,13 @@ public class ClientTest {
* user denies confirmation, an exception is thrown. * user denies confirmation, an exception is thrown.
* *
* @param agreement * @param agreement
* {@link URI} of the Terms of Service * {@link URI} of the Terms of Service
*/ */
public void acceptAgreement(URI agreement) throws AcmeException { public void acceptAgreement(URI agreement) throws AcmeException {
int option = JOptionPane.showConfirmDialog(null, int option = JOptionPane.showConfirmDialog(null,
"Do you accept the Terms of Service?\n\n" + agreement, "Do you accept the Terms of Service?\n\n" + agreement,
"Accept ToS", "Accept ToS",
JOptionPane.YES_NO_OPTION); JOptionPane.YES_NO_OPTION);
if (option == JOptionPane.NO_OPTION) { if (option == JOptionPane.NO_OPTION) {
throw new AcmeException("User did not accept Terms of Service"); throw new AcmeException("User did not accept Terms of Service");
} }
@ -420,7 +427,7 @@ public class ClientTest {
* Invokes this example. * Invokes this example.
* *
* @param args * @param args
* Domains to get a certificate for * Domains to get a certificate for
*/ */
public static void main(String... args) { public static void main(String... args) {
if (args.length == 0) { if (args.length == 0) {

View File

@ -102,17 +102,6 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>org.shredzone.acme4j.it</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>io.fabric8</groupId> <groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId> <artifactId>docker-maven-plugin</artifactId>

View File

@ -0,0 +1,22 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2020 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
module org.shredzone.acme4j.it {
requires org.shredzone.acme4j;
requires org.shredzone.acme4j.utils;
requires com.github.spotbugs.annotations;
requires org.apache.httpcomponents.httpclient;
requires org.apache.httpcomponents.httpcore;
}

View File

@ -28,22 +28,6 @@
<name>acme4j Utils</name> <name>acme4j Utils</name>
<description>acme4j utilities</description> <description>acme4j utilities</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>org.shredzone.acme4j.utils</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.shredzone.acme4j</groupId> <groupId>org.shredzone.acme4j</groupId>

View File

@ -0,0 +1,23 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2020 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
module org.shredzone.acme4j.utils {
requires org.shredzone.acme4j;
requires com.github.spotbugs.annotations;
requires org.bouncycastle.pkix;
requires org.bouncycastle.provider;
exports org.shredzone.acme4j.util;
}

66
pom.xml
View File

@ -73,9 +73,27 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version> <version>3.8.0</version>
<configuration> <configuration>
<source>1.8</source> <release>8</release>
<target>1.8</target>
</configuration> </configuration>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<release>9</release>
</configuration>
</execution>
<execution>
<id>base-compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<excludes>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>com.github.spotbugs</groupId> <groupId>com.github.spotbugs</groupId>
@ -95,8 +113,8 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version> <version>3.0.0-M4</version>
<configuration> <configuration combine.children="append">
<parallel>classes</parallel> <parallel>classes</parallel>
<threadCount>10</threadCount> <threadCount>10</threadCount>
<excludedGroups>java.net.HttpURLConnection</excludedGroups> <excludedGroups>java.net.HttpURLConnection</excludedGroups>
@ -152,7 +170,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version> <version>3.2.0</version>
<configuration> <configuration>
<doclint>syntax,reference</doclint> <doclint>syntax,reference</doclint>
<linksource>true</linksource> <linksource>true</linksource>
@ -214,42 +232,4 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<!-- Workaround: Java 9's javadoc search is broken if no module is defined -->
<profiles>
<profile>
<id>java-9</id>
<activation>
<jdk>[9,10]</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<additionalJOption combine.self="override">--no-module-directories</additionalJOption>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>java-11</id>
<activation>
<jdk>[11,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<source>8</source>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project> </project>

View File

@ -19,16 +19,16 @@ Latest version: ![maven central](https://shredzone.org/maven-central/org.shredzo
* Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation * Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation
* Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental) * Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental)
* Easy to use Java API * Easy to use Java API
* Requires JRE 8 (update 101) or higher * Requires JRE 8 (update 101) or higher. For building the project, Java 9 or higher is required.
* Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22) * Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22)
* Requires [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home), [Bouncy Castle](https://www.bouncycastle.org/) and [slf4j](http://www.slf4j.org/) as dependencies. If you have other means of generating key pairs and CSRs, you can even do without `acme4j-utils` and Bouncy Castle as dependency. * Requires [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home), [Bouncy Castle](https://www.bouncycastle.org/) and [slf4j](http://www.slf4j.org/) as dependencies. If you have other means of generating key pairs and CSRs, you can even do without `acme4j-utils` and Bouncy Castle as dependency.
* Extensive unit and integration tests * Extensive unit and integration tests
## Quick Start ## Quick Start
[This source code](acme4j-example/apidocs/src-html/org/shredzone/acme4j/ClientTest.html) gives an example of how to get a TLS certificate with _acme4j_. [This source code](acme4j-example/apidocs/src-html/org/shredzone/acme4j/example/ClientTest.html) gives an example of how to get a TLS certificate with _acme4j_.
You can run the `org.shredzone.acme4j.ClientTest` class in your IDE, with the domains to be registered as parameters. The test client can also be invoked via maven in a command line: You can run the `org.shredzone.acme4j.example.ClientTest` class in your IDE, with the domains to be registered as parameters. The test client can also be invoked via maven in a command line:
```sh ```sh
mvn exec:java -Dexec.args="example.com example.org" mvn exec:java -Dexec.args="example.com example.org"
@ -51,7 +51,7 @@ acme4j-utils
The Java module name is `org.shredzone.acme4j.utils`. The Java module name is `org.shredzone.acme4j.utils`.
acme4j-example acme4j-example
: This module only contains [an example code](https://shredzone.org/maven/acme4j/acme4j-example/apidocs/src-html/org/shredzone/acme4j/ClientTest.html) that demonstrates how to get a certificate with _acme4j_. It depends on `acme4j-client` and `acme4j-utils`. It is not useful as a dependency in other projects. : This module only contains [an example code](https://shredzone.org/maven/acme4j/acme4j-example/apidocs/src-html/org/shredzone/acme4j/example/ClientTest.html) that demonstrates how to get a certificate with _acme4j_. It depends on `acme4j-client` and `acme4j-utils`. It is not useful as a dependency in other projects.
acme4j-it acme4j-it
: [`acme4j-it`](https://mvnrepository.com/artifact/org.shredzone.acme4j/acme4j-it/latest) mainly serves as integration test suite for _acme4j_ itself. It is not really useful as a dependency in other projects. However if you write own integration tests using [pebble](https://github.com/letsencrypt/pebble) and [pebble-challtestsrv](https://hub.docker.com/r/letsencrypt/pebble-challtestsrv), you may find the [`challtestsrv` configuration client](https://shredzone.org/maven/acme4j/acme4j-it/apidocs/org/shredzone/acme4j/it/BammBammClient.html) useful in your project. : [`acme4j-it`](https://mvnrepository.com/artifact/org.shredzone.acme4j/acme4j-it/latest) mainly serves as integration test suite for _acme4j_ itself. It is not really useful as a dependency in other projects. However if you write own integration tests using [pebble](https://github.com/letsencrypt/pebble) and [pebble-challtestsrv](https://hub.docker.com/r/letsencrypt/pebble-challtestsrv), you may find the [`challtestsrv` configuration client](https://shredzone.org/maven/acme4j/acme4j-it/apidocs/org/shredzone/acme4j/it/BammBammClient.html) useful in your project.

View File

@ -4,6 +4,8 @@ This document will help you migrate your code to the latest _acme4j_ version.
## Migration to Version 2.10 ## Migration to Version 2.10
- acme4j now provides real `module-info.java` definitions. It also means that for _building_ this project, Java 9 is the minimum requirement now.
- In a preparation for Java 9 modules, the JSR305 null-safe annotations have been replaced by SpotBugs annotations. This _should_ have no impact on your code, as the method signatures themselves are unchanged. However, the compiler could now complain about some `null` dereferences that have been undetected before. Reason is that JSR305 uses the `javax.annotations` package, which leads to split packages in a Java 9 modular environment. - In a preparation for Java 9 modules, the JSR305 null-safe annotations have been replaced by SpotBugs annotations. This _should_ have no impact on your code, as the method signatures themselves are unchanged. However, the compiler could now complain about some `null` dereferences that have been undetected before. Reason is that JSR305 uses the `javax.annotations` package, which leads to split packages in a Java 9 modular environment.
- When fetching the directory, acme4j now evaluates HTTP caching headers instead of just caching the directory for 1 hour. However, Let's Encrypt explicitly forbids caching, which means that a fresh copy of the directory is now fetched from the server every time it is needed. I don't like it, but it is the RFC conformous behavior. It needs to be [fixed on Let's Encrypt side](https://github.com/letsencrypt/boulder/issues/4814). - When fetching the directory, acme4j now evaluates HTTP caching headers instead of just caching the directory for 1 hour. However, Let's Encrypt explicitly forbids caching, which means that a fresh copy of the directory is now fetched from the server every time it is needed. I don't like it, but it is the RFC conformous behavior. It needs to be [fixed on Let's Encrypt side](https://github.com/letsencrypt/boulder/issues/4814).