You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by rm...@apache.org on 2018/07/06 08:33:28 UTC

svn commit: r1835215 - in /openwebbeans/meecrowave/trunk: ./ meecrowave-arquillian/ meecrowave-core/ meecrowave-doc/ meecrowave-gradle-plugin/ meecrowave-jolokia/ meecrowave-jpa/ meecrowave-jta/ meecrowave-letsencrypt/ meecrowave-letsencrypt/src/ meecr...

Author: rmannibucau
Date: Fri Jul  6 08:33:27 2018
New Revision: 1835215

URL: http://svn.apache.org/viewvc?rev=1835215&view=rev
Log:
MEECROWAVE-125 add Automatic-Module-Name in jars

Added:
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptReloadLifecycle.java
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptSetup.java
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptValve.java
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/META-INF/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/META-INF/services/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/META-INF/services/org.apache.meecrowave.runner.Cli$Options
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/test/
    openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/test/java/
Modified:
    openwebbeans/meecrowave/trunk/meecrowave-arquillian/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-core/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-doc/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-gradle-plugin/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-jolokia/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-jta/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-maven-plugin/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-oauth2/pom.xml
    openwebbeans/meecrowave/trunk/meecrowave-specs-api/pom.xml
    openwebbeans/meecrowave/trunk/pom.xml

Modified: openwebbeans/meecrowave/trunk/meecrowave-arquillian/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-arquillian/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-arquillian/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-arquillian/pom.xml Fri Jul  6 08:33:27 2018
@@ -30,6 +30,7 @@
 
   <properties>
     <arquillian.version>1.1.13.Final</arquillian.version>
+    <meecrowave.build.name>${project.groupId}.arquillian</meecrowave.build.name>
   </properties>
 
   <dependencies>

Modified: openwebbeans/meecrowave/trunk/meecrowave-core/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-core/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-core/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-core/pom.xml Fri Jul  6 08:33:27 2018
@@ -28,6 +28,10 @@
   <artifactId>meecrowave-core</artifactId>
   <name>Meecrowave :: Core</name>
 
+  <properties>
+    <meecrowave.build.name>${project.groupId}.core</meecrowave.build.name>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>org.apache.meecrowave</groupId>

Modified: openwebbeans/meecrowave/trunk/meecrowave-doc/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-doc/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-doc/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-doc/pom.xml Fri Jul  6 08:33:27 2018
@@ -32,6 +32,7 @@
   <properties>
     <jbake.http>false</jbake.http>
     <jbake.pdf>true</jbake.pdf>
+    <meecrowave.build.name>${project.groupId}.documentation</meecrowave.build.name>
   </properties>
 
   <dependencies>

Modified: openwebbeans/meecrowave/trunk/meecrowave-gradle-plugin/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-gradle-plugin/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-gradle-plugin/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-gradle-plugin/pom.xml Fri Jul  6 08:33:27 2018
@@ -30,6 +30,7 @@
 
   <properties>
     <gradle.version>3.1</gradle.version>
+    <meecrowave.build.name>${project.groupId}.gradle</meecrowave.build.name>
   </properties>
 
   <dependencies>

Modified: openwebbeans/meecrowave/trunk/meecrowave-jolokia/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-jolokia/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-jolokia/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-jolokia/pom.xml Fri Jul  6 08:33:27 2018
@@ -30,6 +30,7 @@
 
   <properties>
     <jolokia.version>2.0.0-M3</jolokia.version>
+    <meecrowave.build.name>${project.groupId}.jolokia</meecrowave.build.name>
   </properties>
 
   <dependencies>

Modified: openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-jpa/pom.xml Fri Jul  6 08:33:27 2018
@@ -28,6 +28,10 @@
   <artifactId>meecrowave-jpa</artifactId>
   <name>Meecrowave :: JPA</name>
 
+  <properties>
+    <meecrowave.build.name>${project.groupId}.jpa</meecrowave.build.name>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>

Modified: openwebbeans/meecrowave/trunk/meecrowave-jta/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-jta/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-jta/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-jta/pom.xml Fri Jul  6 08:33:27 2018
@@ -28,6 +28,10 @@
   <artifactId>meecrowave-jta</artifactId>
   <name>Meecrowave :: JTA</name>
 
+  <properties>
+    <meecrowave.build.name>${project.groupId}.jta</meecrowave.build.name>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>

Added: openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/pom.xml?rev=1835215&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/pom.xml (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/pom.xml Fri Jul  6 08:33:27 2018
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>meecrowave</artifactId>
+    <groupId>org.apache.meecrowave</groupId>
+    <version>1.2.2-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>meecrowave-letsencrypt</artifactId>
+  <name>Meecrowave :: Let's Encrypt</name>
+
+  <properties>
+    <acme4j.version>2.1</acme4j.version>
+    <meecrowave.build.name>${project.groupId}.letsencrypt</meecrowave.build.name>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.meecrowave</groupId>
+      <artifactId>meecrowave-core</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tomcat</groupId>
+      <artifactId>tomcat-catalina</artifactId>
+      <version>${tomcat.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- todo: replace by jaxrs 2 client + jsonb -->
+    <dependency>
+      <groupId>org.shredzone.acme4j</groupId>
+      <artifactId>acme4j-client</artifactId>
+      <version>${acme4j.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.shredzone.acme4j</groupId>
+      <artifactId>acme4j-utils</artifactId>
+      <version>${acme4j.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+</project>

Added: openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptReloadLifecycle.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptReloadLifecycle.java?rev=1835215&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptReloadLifecycle.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptReloadLifecycle.java Fri Jul  6 08:33:27 2018
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.meecrowave.letencrypt;
+
+import static java.util.Optional.ofNullable;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.stream.Stream;
+
+import org.apache.coyote.http11.AbstractHttp11Protocol;
+import org.apache.meecrowave.logging.tomcat.LogFacade;
+import org.apache.meecrowave.runner.Cli;
+import org.apache.meecrowave.runner.cli.CliOption;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+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.Http01Challenge;
+import org.shredzone.acme4j.exception.AcmeException;
+import org.shredzone.acme4j.util.CSRBuilder;
+
+// we depend on bouncycastle but user myst add it to be able to use that
+// todo: check we can get rid of it and use jaxrs client instead of acme lib
+public class LetsEncryptReloadLifecycle implements AutoCloseable, Runnable {
+
+    private final AtomicReference<LogFacade> logger = new AtomicReference<>();
+
+    private final AbstractHttp11Protocol<?> protocol;
+
+    private final ScheduledExecutorService thread;
+
+    private final ScheduledFuture<?> refreshTask;
+
+    private final LetsEncryptConfig config;
+
+    private final BiConsumer<String, String> challengeUpdater;
+
+    public LetsEncryptReloadLifecycle(final LetsEncryptConfig config, final AbstractHttp11Protocol<?> protocol,
+                                      final BiConsumer<String, String> challengeUpdater) {
+        this.config = config;
+        this.config.init();
+        this.protocol = protocol;
+        this.challengeUpdater = challengeUpdater;
+
+        final SecurityManager s = System.getSecurityManager();
+        final ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+        this.thread = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+
+            @Override
+            public Thread newThread(final Runnable r) {
+                final Thread newThread = new Thread(group, r, LetsEncryptReloadLifecycle.class.getName() + "_" + hashCode());
+                newThread.setDaemon(false);
+                newThread.setPriority(Thread.NORM_PRIORITY);
+                newThread.setContextClassLoader(getClass().getClassLoader());
+                return newThread;
+            }
+        });
+        refreshTask = this.thread.scheduleAtFixedRate(this, 0L, config.getRefreshInterval(), TimeUnit.SECONDS);
+    }
+
+    @Override
+    public synchronized void run() {
+        final KeyPair userKeyPair = loadOrCreateKeyPair(config.getUserKeySize(), config.getUserKeyLocation());
+        final KeyPair domainKeyPair = loadOrCreateKeyPair(config.getDomainKeySize(), config.getDomainKey());
+
+        final Session session = new Session(config.getEndpoint());
+        try {
+            final Account account = new AccountBuilder().agreeToTermsOfService().useKeyPair(userKeyPair).create(session);
+            final Order order = account.newOrder().domains(config.getDomains().trim().split(",")).create();
+            final boolean updated = order.getAuthorizations().stream().map(authorization -> {
+                try {
+                    return authorize(authorization);
+                } catch (final AcmeException e) {
+                    getLogger().error(e.getMessage(), e);
+                    return false;
+                }
+            }).reduce(false, (previous, val) -> previous || val);
+            if (!updated) {
+                return;
+            }
+
+            final CSRBuilder csrBuilder = new CSRBuilder();
+            csrBuilder.addDomains(config.getDomains());
+            csrBuilder.sign(domainKeyPair);
+
+            try (final Writer writer = new BufferedWriter(new FileWriter(config.getDomainCertificate()))) {
+                csrBuilder.write(writer);
+            }
+
+            order.execute(csrBuilder.getEncoded());
+
+            try {
+                int attempts = config.getRetryCount();
+                while (order.getStatus() != Status.VALID && attempts-- > 0) {
+                    if (order.getStatus() == Status.INVALID) {
+                        throw new AcmeException("Order failed... Giving up.");
+                    }
+                    Thread.sleep(config.getRetryTimeoutMs());
+                    order.update();
+                }
+            } catch (final InterruptedException ex) {
+                getLogger().error(ex.getMessage());
+                Thread.currentThread().interrupt();
+                return;
+            }
+
+            final Certificate certificate = order.getCertificate();
+            getLogger().info("Got new certificate " + certificate.getLocation() + " for domain(s) " + config.getDomains());
+
+            try (final Writer writer = new BufferedWriter(new FileWriter(config.getDomainChain()))) {
+                certificate.writeCertificate(writer);
+            }
+
+            protocol.reloadSsslHostConfigs();
+        } catch (final AcmeException | IOException ex) {
+            getLogger().error(ex.getMessage(), ex);
+        }
+    }
+
+    private LogFacade getLogger() {
+        LogFacade logFacade = logger.get();
+        if (logFacade == null) {
+            logFacade = new LogFacade(getClass().getName());
+            // ok to use 2 instances since the backing instance will be the same, so ignore returned value
+            logger.compareAndSet(null, logFacade);
+        }
+        return logFacade;
+    }
+
+    @Override
+    public void close() {
+        ofNullable(refreshTask).ifPresent(t -> t.cancel(true));
+        ofNullable(thread).ifPresent(ExecutorService::shutdownNow);
+        try {
+            thread.awaitTermination(5, TimeUnit.SECONDS);
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    private boolean authorize(final Authorization authorization) throws AcmeException {
+        final Challenge challenge = httpChallenge(authorization);
+        if (challenge == null) {
+            throw new AcmeException("HTTP challenge is null");
+        }
+        if (challenge.getStatus() == Status.VALID) {
+            return false;
+        }
+
+        challenge.trigger();
+
+        try {
+            int attempts = config.getRetryCount();
+            while (challenge.getStatus() != Status.VALID && attempts-- > 0) {
+                if (challenge.getStatus() == Status.INVALID) {
+                    throw new AcmeException("Invalid challenge status, exiting refresh iteration");
+                }
+
+                Thread.sleep(config.getRetryTimeoutMs());
+                challenge.update();
+            }
+        } catch (final InterruptedException ex) {
+            Thread.currentThread().interrupt();
+        }
+
+        if (challenge.getStatus() != Status.VALID) {
+            throw new AcmeException("Challenge for domain " + authorization.getDomain() + ", is invalid, exiting iteration");
+        }
+        return true;
+    }
+
+    private Challenge httpChallenge(final Authorization auth) throws AcmeException {
+        final Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);
+        if (challenge == null) {
+            throw new AcmeException("Challenge is null");
+        }
+
+        challengeUpdater.accept("/.well-known/acme-challenge/" + challenge.getToken(), challenge.getAuthorization());
+        return challenge;
+    }
+
+    private KeyPair loadOrCreateKeyPair(final int keySize, final File file) {
+        if (file.exists()) {
+            try (final PEMParser parser = new PEMParser(new FileReader(file))) {
+                return new JcaPEMKeyConverter().getKeyPair(PEMKeyPair.class.cast(parser.readObject()));
+            } catch (final IOException ex) {
+                throw new IllegalStateException("Can't read PEM file: " + file, ex);
+            }
+        } else {
+            try {
+                final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+                keyGen.initialize(keySize);
+                final KeyPair keyPair = keyGen.generateKeyPair();
+                try (final JcaPEMWriter writer = new JcaPEMWriter(new FileWriter(file))) {
+                    writer.writeObject(keyPair);
+                } catch (final IOException ex) {
+                    throw new IllegalStateException("Can't read PEM file: " + file, ex);
+                }
+                return keyPair;
+            } catch (final NoSuchAlgorithmException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+
+    public static class LetsEncryptConfig implements Cli.Options {
+
+        @CliOption(name = "letsencrypt-refresh-interval", description = "Number of second between let'sencrypt refreshes")
+        private long refreshInterval = 60;
+
+        @CliOption(name = "letsencrypt-domains", description = "Comma separated list of domains to manage")
+        private String domains;
+
+        @CliOption(name = "letsencrypt-key-user-location", description = "Where the user key must be stored")
+        private File userKeyLocation;
+
+        @CliOption(name = "letsencrypt-key-user-size", description = "User key size")
+        private int userKeySize = 2048;
+
+        @CliOption(name = "letsencrypt-key-domain-location", description = "Where the domain key must be stored")
+        private File domainKey;
+
+        @CliOption(name = "letsencrypt-key-domain-size", description = "Domain key size")
+        private int domainKeySize = 2048;
+
+        @CliOption(name = "letsencrypt-certificate-domain-location", description = "Where the domain certificate must be stored")
+        private File domainCertificate;
+
+        @CliOption(name = "letsencrypt-chain-domain-location", description = "Where the domain chain must be stored")
+        private File domainChain;
+
+        @CliOption(name = "letsencrypt-endpoint", description = "Endpoint to use to get the certificates")
+        private String endpoint;
+
+        @CliOption(name = "letsencrypt-endpoint-staging", description = "Ignore if endpoint is set, otherwise it set the endpoint accordingly")
+        private boolean staging = false;
+
+        @CliOption(name = "letsencrypt-retry-timeout-ms", description = "How long to wait before retrying to get the certificate, default is 3s")
+        private long retryTimeoutMs = 3000;
+
+        @CliOption(name = "letsencrypt-retry-count", description = "How many retries to do")
+        private int retryCount = 20;
+
+        public void init() {
+            if (userKeyLocation == null) {
+                userKeyLocation = new File(System.getProperty("catalina.base"), "conf/letsencrypt/user.key");
+            }
+            if (domainKey == null) {
+                domainKey = new File(System.getProperty("catalina.base"), "conf/letsencrypt/domain.key");
+            }
+            if (domainCertificate == null) {
+                domainCertificate = new File(System.getProperty("catalina.base"), "conf/letsencrypt/domain.csr");
+            }
+            if (domainChain == null) {
+                domainChain = new File(System.getProperty("catalina.base"), "conf/letsencrypt/domain.chain.csr");
+            }
+            if (endpoint == null) {
+                if (isStaging()) {
+                    endpoint = "https://acme-staging-v02.api.letsencrypt.org/directory";
+                } else {
+                    endpoint = "https://acme-v02.api.letsencrypt.org/directory";
+                }
+            }
+            Stream.of(userKeyLocation, domainKey, domainCertificate, domainChain).map(File::getAbsoluteFile)
+                    .map(File::getParentFile).filter(Objects::nonNull).distinct().forEach(File::mkdirs);
+        }
+
+        public boolean isStaging() {
+            return staging;
+        }
+
+        public int getRetryCount() {
+            return retryCount;
+        }
+
+        public int getDomainKeySize() {
+            return domainKeySize;
+        }
+
+        public String getEndpoint() {
+            return endpoint;
+        }
+
+        public long getRefreshInterval() {
+            return refreshInterval;
+        }
+
+        public String getDomains() {
+            return domains;
+        }
+
+        public File getUserKeyLocation() {
+            return userKeyLocation;
+        }
+
+        public int getUserKeySize() {
+            return userKeySize;
+        }
+
+        public File getDomainKey() {
+            return domainKey;
+        }
+
+        public File getDomainCertificate() {
+            return domainCertificate;
+        }
+
+        public File getDomainChain() {
+            return domainChain;
+        }
+
+        public long getRetryTimeoutMs() {
+            return retryTimeoutMs;
+        }
+    }
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptSetup.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptSetup.java?rev=1835215&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptSetup.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptSetup.java Fri Jul  6 08:33:27 2018
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.meecrowave.letencrypt;
+
+import org.apache.catalina.startup.Tomcat;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.http11.AbstractHttp11Protocol;
+import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.logging.tomcat.LogFacade;
+
+public class LetsEncryptSetup implements Meecrowave.MeecrowaveAwareInstanceCustomizer {
+    private Meecrowave instance;
+
+    @Override
+    public void accept(final Tomcat tomcat) {
+        final ProtocolHandler protocolHandler = tomcat.getConnector().getProtocolHandler();
+        if (!AbstractHttp11Protocol.class.isInstance(protocolHandler)) {
+            return;
+        }
+
+        final LetsEncryptReloadLifecycle.LetsEncryptConfig config = instance.getConfiguration()
+            .getExtension(LetsEncryptReloadLifecycle.LetsEncryptConfig.class);
+        if (config.getDomains() == null || config.getDomains().trim().isEmpty()) {
+            return;
+        }
+
+        new LogFacade(getClass().getName()).info("Let's Encrypt extension activated");
+        tomcat.getHost().getPipeline().addValve(new LetsEncryptValve(AbstractHttp11Protocol.class.cast(protocolHandler), config));
+    }
+
+    @Override
+    public void setMeecrowave(final Meecrowave meecrowave) {
+        instance = meecrowave;
+    }
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptValve.java
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptValve.java?rev=1835215&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptValve.java (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/java/org/apache/meecrowave/letencrypt/LetsEncryptValve.java Fri Jul  6 08:33:27 2018
@@ -0,0 +1,60 @@
+package org.apache.meecrowave.letencrypt;
+
+import static java.util.Optional.ofNullable;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.coyote.http11.AbstractHttp11Protocol;
+
+public class LetsEncryptValve extends ValveBase {
+    private final AbstractHttp11Protocol<?> protocol;
+    private final LetsEncryptReloadLifecycle.LetsEncryptConfig config;
+    private LetsEncryptReloadLifecycle support;
+
+    private volatile Current current = new Current("/.well-known/acme-challenge/-", "none");
+
+    public LetsEncryptValve(final AbstractHttp11Protocol<?> protocol,
+                            final LetsEncryptReloadLifecycle.LetsEncryptConfig config) {
+        super(true);
+        this.protocol = protocol;
+        this.config = config;
+    }
+
+    @Override
+    protected void initInternal() throws LifecycleException {
+        super.initInternal();
+        support = new LetsEncryptReloadLifecycle(config, protocol, (e, c) -> current = new Current(e, c));
+    }
+
+    @Override
+    protected synchronized void stopInternal() throws LifecycleException {
+        super.stopInternal();
+        ofNullable(support).ifPresent(LetsEncryptReloadLifecycle::close);
+    }
+
+    @Override
+    public void invoke(final Request request, final Response response) throws IOException, ServletException {
+        if (request.getRequestURI().equals(current.endpoint)) {
+            response.setHeader("Content-Type", "text/plain");
+            response.getWriter().write(current.challenge);
+            return;
+        }
+        getNext().invoke(request, response);
+    }
+
+    private static class Current {
+        private final String endpoint;
+        private final String challenge;
+
+        private Current(final String endpoint, final String challenge) {
+            this.endpoint = endpoint;
+            this.challenge = challenge;
+        }
+    }
+}

Added: openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/META-INF/services/org.apache.meecrowave.runner.Cli$Options
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/META-INF/services/org.apache.meecrowave.runner.Cli%24Options?rev=1835215&view=auto
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/META-INF/services/org.apache.meecrowave.runner.Cli$Options (added)
+++ openwebbeans/meecrowave/trunk/meecrowave-letsencrypt/src/main/resources/META-INF/services/org.apache.meecrowave.runner.Cli$Options Fri Jul  6 08:33:27 2018
@@ -0,0 +1 @@
+org.apache.meecrowave.letencrypt.LetsEncryptReloadLifecycle$LetsEncryptConfig

Modified: openwebbeans/meecrowave/trunk/meecrowave-maven-plugin/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-maven-plugin/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-maven-plugin/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-maven-plugin/pom.xml Fri Jul  6 08:33:27 2018
@@ -31,6 +31,7 @@
 
   <properties>
     <maven.version>3.3.9</maven.version>
+    <meecrowave.build.name>${project.groupId}.maven</meecrowave.build.name>
   </properties>
 
   <dependencies>

Modified: openwebbeans/meecrowave/trunk/meecrowave-oauth2/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-oauth2/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-oauth2/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-oauth2/pom.xml Fri Jul  6 08:33:27 2018
@@ -28,6 +28,10 @@
   <artifactId>meecrowave-oauth2</artifactId>
   <name>Meecrowave :: OAuth2</name>
 
+  <properties>
+    <meecrowave.build.name>${project.groupId}.oauth2</meecrowave.build.name>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>org.apache.meecrowave</groupId>

Modified: openwebbeans/meecrowave/trunk/meecrowave-specs-api/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/meecrowave-specs-api/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/meecrowave-specs-api/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/meecrowave-specs-api/pom.xml Fri Jul  6 08:33:27 2018
@@ -28,6 +28,10 @@
   <artifactId>meecrowave-specs-api</artifactId>
   <name>Meecrowave :: Specs API</name>
 
+  <properties>
+    <meecrowave.build.name>${project.groupId}.specs</meecrowave.build.name>
+  </properties>
+
   <dependencies>
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>

Modified: openwebbeans/meecrowave/trunk/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/meecrowave/trunk/pom.xml?rev=1835215&r1=1835214&r2=1835215&view=diff
==============================================================================
--- openwebbeans/meecrowave/trunk/pom.xml (original)
+++ openwebbeans/meecrowave/trunk/pom.xml Fri Jul  6 08:33:27 2018
@@ -47,6 +47,7 @@
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <meecrowave.build.name>${project.groupId}.${project.artifactId}</meecrowave.build.name>
 
     <junit.version>4.12</junit.version>
     <tomcat.version>9.0.10</tomcat.version>
@@ -65,6 +66,7 @@
   </properties>
 
   <modules>
+    <module>meecrowave-specs-api</module>
     <module>meecrowave-core</module>
     <module>meecrowave-maven-plugin</module>
     <module>meecrowave-gradle-plugin</module>
@@ -76,7 +78,7 @@
     <module>meecrowave-jta</module>
     <module>integration-tests</module>
     <module>meecrowave-oauth2</module>
-    <module>meecrowave-specs-api</module>
+    <module>meecrowave-letsencrypt</module>
   </modules>
 
   <dependencyManagement>
@@ -141,9 +143,24 @@
         </configuration>
       </plugin>
 
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>3.0.2</version>
+        <configuration>
+          <archive combine.children="append">
+            <manifestEntries>
+              <Automatic-Module-Name>${meecrowave.build.name}</Automatic-Module-Name>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.22.0</version>
         <configuration>
           <trimStackTrace>false</trimStackTrace>
         </configuration>