/*
 * Copyright 2016 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *      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.kie.server.router;

import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;

import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.PathHandler;
import io.undertow.server.handlers.ResponseCodeHandler;
import io.undertow.server.handlers.proxy.ProxyHandler;
import org.jboss.logging.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.kie.server.router.handlers.AdminHttpHandler;
import org.kie.server.router.handlers.ContainersHttpHandler;
import org.kie.server.router.handlers.DocumentsHttpHandler;
import org.kie.server.router.handlers.JobsHttpHandler;
import org.kie.server.router.handlers.KieServerInfoHandler;
import org.kie.server.router.handlers.QueriesDataHttpHandler;
import org.kie.server.router.handlers.QueriesHttpHandler;
import org.kie.server.router.proxy.KieServerProxyClient;
import org.kie.server.router.repository.FileRepository;
import org.kie.server.router.spi.ConfigRepository;
import org.kie.server.router.utils.HttpUtils;

public class KieServerRouter {
    private static final String HOST = System.getProperty(KieServerRouterConstants.ROUTER_HOST, "localhost");
    private static final String PORT = System.getProperty(KieServerRouterConstants.ROUTER_PORT, "9000");

    private static final String CONTROLLER = System.getProperty(KieServerRouterConstants.CONTROLLER);
    
    private static final Logger log = Logger.getLogger(KieServerRouter.class);

    private static final String SERVER_INFO_JSON = "{\n"+
            "      \"version\" : \"LATEST\",\n"+
            "      \"name\" : \"KIE Server Router\",\n"+
            "      \"location\" : \"" + KieServerInfoHandler.getLocationUrl() + "\",\n"+
            "      \"capabilities\" : [ \"KieServer\", \"BRM\", \"BPM\", \"CaseMgmt\", \"BPM-UI\", \"BRP\" ],\n"+
            "      \"id\" : \"kie-server-router\"\n"+
            "}";

    private ServiceLoader<ConfigRepository> configRepositoryServiceLoader = ServiceLoader.load(ConfigRepository.class);
    
    private Undertow server;
    private ConfigRepository repository = new FileRepository();

    public KieServerRouter() {
        configRepositoryServiceLoader.forEach( repo -> repository = repo);
        log.info("KIE Server router repository implementation is " + repository);
    }

    public static void main(String[] args) {
        KieServerRouter router = new KieServerRouter();        
        router.start(HOST, Integer.parseInt(PORT));

        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                router.stop();
            }
        });
    }
    
    public void start(String host, Integer port) {
        System.setProperty(KieServerRouterConstants.ROUTER_HOST, host);
        System.setProperty(KieServerRouterConstants.ROUTER_PORT, port.toString());

        Configuration configuration = repository.load();
        if (configuration == null) {
            configuration = new Configuration();
        }
        final KieServerProxyClient proxyClient = new KieServerProxyClient(configuration);
        Map<String, Set<String>> perContainer = configuration.getHostsPerContainer();

        for (Map.Entry<String, Set<String>> entry : perContainer.entrySet()) {
            entry.getValue().forEach(url -> {
                proxyClient.addContainer(entry.getKey(), URI.create(url));
            });
        }
        
        HttpHandler notFoundHandler = ResponseCodeHandler.HANDLE_404;        
        AdminHttpHandler adminHandler = new AdminHttpHandler(configuration, repository);

        PathHandler pathHandler = Handlers.path(new ProxyHandler(proxyClient, notFoundHandler));
        pathHandler.addPrefixPath("/queries/definitions", new QueriesDataHttpHandler(notFoundHandler, adminHandler));
        pathHandler.addPrefixPath("/queries", new QueriesHttpHandler(notFoundHandler, adminHandler));
        pathHandler.addPrefixPath("/jobs", new JobsHttpHandler(notFoundHandler, adminHandler));
        pathHandler.addPrefixPath("/documents", new DocumentsHttpHandler(notFoundHandler, adminHandler));
        pathHandler.addExactPath("/containers", new ContainersHttpHandler(notFoundHandler, adminHandler));
        pathHandler.addPrefixPath("/admin", adminHandler);
        pathHandler.addExactPath("/", new KieServerInfoHandler());
   
        // main server configuration
        server = Undertow.builder()
                .addHttpListener(port, host)                
                .setHandler(pathHandler)
                .build();
        server.start();
        log.infof("KieServerRouter started on %s:%s at %s", host, port, new Date());
        connectToController(adminHandler);

    }

    public void stop() {
        stop(false);
    }
    
    public void stop(boolean clean) {
        disconnectToController();
        if (server != null) {
            server.stop();
            if (clean) {
                repository.clean();
            }
            log.infof("KieServerRouter stopped on %s:%s at %s", System.getProperty(KieServerRouterConstants.ROUTER_HOST), System.getProperty(KieServerRouterConstants.ROUTER_PORT), new Date());
        } else {
            log.error("KieServerRouter was not started");
        }
    }

    protected void connectToController(AdminHttpHandler adminHandler) {
        if (CONTROLLER == null) {
            return;
        }
        try {
            String jsonResponse = HttpUtils.putHttpCall(CONTROLLER + "/server/kie-server-router", SERVER_INFO_JSON);
            log.debugf("Controller response :: ", jsonResponse);
            List<String> containers = new ArrayList<>();

            JSONObject serverConfig = new JSONObject(jsonResponse);
            try {
                JSONArray sourceList = serverConfig.getJSONArray("containers");

                for (int i = 0; i < sourceList.length(); i++) {
                    JSONObject container = sourceList.getJSONObject(i);
                    containers.add(container.getString("container-id"));
                }
            } catch (JSONException e) {
                // if the server template did not exist the containers can be null, meaning not JSONArray
                log.debug("Error when getting list of containers:: " + e.getMessage(), e);
            }

            adminHandler.addControllerContainers(containers);

            log.infof("KieServerRouter connected to controller at " + CONTROLLER);
        } catch (Exception e) {
            log.error("Error when connecting to controller at " + CONTROLLER, e);
        }
    }

    protected void disconnectToController() {
        if (CONTROLLER == null) {
            return;
        }
        try {
            HttpUtils.deleteHttpCall(CONTROLLER + "/server/kie-server-router/?location="+ URLEncoder.encode(KieServerInfoHandler.getLocationUrl(), "UTF-8"));
            log.infof("KieServerRouter disconnected from controller at " + CONTROLLER);
        } catch (Exception e) {
            log.error("Error when disconnecting from controller at " + CONTROLLER, e);
        }
    }

}

@@ -892,32 +892,84 @@
 er.HttpHandler;%0A
+import io.undertow.server.handlers.BlockingHandler;%0A
 import io.undert
@@ -5154,19 +5154,89 @@
 ler());%0A
+%0A
    
+     HttpHandler blockingHandler = new BlockingHandler(pathHandler);%0A
 %0A       
@@ -5382,36 +5382,40 @@
     .setHandler(
-path
+blocking
 Handler)%0A       

Use blocking mode for router (#965)