/*
 * acme4j - Java ACME client
 *
 * Copyright (C) 2015 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.
 */
package org.shredzone.acme4j.provider;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.challenge.OutOfBand01Challenge;
import org.shredzone.acme4j.challenge.TlsSni02Challenge;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.DefaultConnection;
import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.util.JSON;

/**
 * Abstract implementation of {@link AcmeProvider}. It consists of a challenge
 * registry and a standard {@link HttpConnector}.
 * <p>
 * Implementing classes must implement at least {@link AcmeProvider#accepts(URI)}
 * and {@link AbstractAcmeProvider#resolve(URI)}.
 */
public abstract class AbstractAcmeProvider implements AcmeProvider {

    private static final Map<String, Constructor<? extends Challenge>> CHALLENGES = challengeMap();

    @Override
    public Connection connect() {
        return new DefaultConnection(createHttpConnector());
    }

    @Override
    public JSON directory(Session session, URI serverUri) throws AcmeException {
        try (Connection conn = connect()) {
            conn.sendRequest(resolve(serverUri), session);
            conn.accept(HttpURLConnection.HTTP_OK);

            // use nonce header if there is one, saves a HEAD request...
            conn.updateSession(session);

            return conn.readJsonResponse();
        }
    }

    @SuppressWarnings("deprecation") // must still provide deprecated challenges
    private static Map<String, Constructor<? extends Challenge>> challengeMap() {
        Map<String, Constructor<? extends Challenge>> map = new HashMap<>();

        try {
            map.put(Dns01Challenge.TYPE,
                            Dns01Challenge.class.getConstructor(Session.class));

            map.put(org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE,
                            org.shredzone.acme4j.challenge.TlsSni01Challenge.class.getConstructor(Session.class));

            map.put(TlsSni02Challenge.TYPE,
                            TlsSni02Challenge.class.getConstructor(Session.class));

            map.put(Http01Challenge.TYPE,
                            Http01Challenge.class.getConstructor(Session.class));

            map.put(OutOfBand01Challenge.TYPE,
                            OutOfBand01Challenge.class.getConstructor(Session.class));
        } catch (NoSuchMethodException ex) {
            throw new IllegalStateException("Could not find Challenge constructor", ex);
        }

        return Collections.unmodifiableMap(map);
    }

    @Override
    public Challenge createChallenge(Session session, String type) {
        Objects.requireNonNull(session, "session");
        Objects.requireNonNull(type, "type");

        Constructor<? extends Challenge> constructor = CHALLENGES.get(type);
        if (constructor == null) {
            return null;
        }

        try {
            return constructor.newInstance(session);
        } catch (InvocationTargetException | IllegalAccessException
                    | InstantiationException ex) {
            throw new IllegalStateException(
                    "Could not instantiate a Challenge for type " + type, ex);
        }
    }

    /**
     * Creates a {@link HttpConnector}.
     * <p>
     * Subclasses may override this method to configure the {@link HttpConnector}.
     */
    protected HttpConnector createHttpConnector() {
        return new HttpConnector();
    }

}

@@ -484,98 +484,8 @@
 r;%0A%0A
-import java.lang.reflect.Constructor;%0Aimport java.lang.reflect.InvocationTargetException;%0A
 impo
@@ -639,16 +639,52 @@
 Objects;
+%0Aimport java.util.function.Function;
 %0A%0Aimport
@@ -1625,37 +1625,33 @@
 String, 
-Constructor%3C? extends
+Function%3CSession,
  Challen
@@ -2340,37 +2340,33 @@
 String, 
-Constructor%3C? extends
+Function%3CSession,
  Challen
@@ -2407,37 +2407,33 @@
 String, 
-Constructor%3C? extends
+Function%3CSession,
  Challen
@@ -2461,34 +2461,16 @@
 p%3C%3E();%0A%0A
-        try %7B%0A    
         
@@ -2501,95 +2501,31 @@
 YPE,
-%0A                            Dns01Challenge.class.getConstructor(Session.class));%0A%0A    
+ Dns01Challenge::new);%0A
     
@@ -2594,36 +2594,8 @@
 YPE,
-%0A                           
  org
@@ -2643,579 +2643,212 @@
 enge
-.class.getConstructor(Session.class));%0A%0A            map.put(TlsSni02Challenge.TYPE,%0A                            TlsSni02Challenge.class.getConstructor(Session.class));%0A%0A            map.put(Http01Challenge.TYPE,%0A                            Http01Challenge.class.getConstructor(Session.class));%0A%0A            map.put(OutOfBand01Challenge.TYPE,%0A                            OutOfBand01Challenge.class.getConstructor(Session.class));%0A        %7D catch (NoSuchMethodException ex) %7B%0A            throw new IllegalStateException(%22Could not find Challenge constructor%22, ex);%0A        %7D
+::new);%0A        map.put(TlsSni02Challenge.TYPE, TlsSni02Challenge::new);%0A        map.put(Http01Challenge.TYPE, Http01Challenge::new);%0A        map.put(OutOfBand01Challenge.TYPE, OutOfBand01Challenge::new);
 %0A%0A  
@@ -3095,29 +3095,25 @@
     
-Constructor%3C? extends
+Function%3CSession,
  Cha
@@ -3231,26 +3231,8 @@
  %7D%0A%0A
-        try %7B%0A    
     
@@ -3258,282 +3258,23 @@
 tor.
-newInstance(session);%0A        %7D catch (InvocationTargetException %7C IllegalAccessException%0A                    %7C InstantiationException ex) %7B%0A            throw new IllegalStateException(%0A                    %22Could not instantiate a Challenge for type %22 + type, ex);%0A        %7D
+apply(session);
 %0A   

Use functional programming for challenge class generation