org.ulyssis.ipp.publisher.HttpServerPublisher.java Source code

Java tutorial

Introduction

Here is the source code for org.ulyssis.ipp.publisher.HttpServerPublisher.java

Source

/*
 * Copyright (C) 2014-2015 ULYSSIS VZW
 *
 * This file is part of i++.
 * 
 * i++ is free software: you can redistribute it and/or modify
 * it under the terms of version 3 of the GNU Affero General Public License
 * as published by the Free Software Foundation. No other versions apply.
 * 
 * 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. See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>
 */
package org.ulyssis.ipp.publisher;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.security.api.AuthenticationMode;
import io.undertow.security.handlers.AuthenticationCallHandler;
import io.undertow.security.handlers.AuthenticationConstraintHandler;
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
import io.undertow.security.handlers.SecurityInitialHandler;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.Credential;
import io.undertow.security.idm.IdentityManager;
import io.undertow.security.impl.ClientCertAuthenticationMechanism;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HeaderValues;
import io.undertow.util.HttpString;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ulyssis.ipp.utils.Serialization;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.Principal;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;

/**
 * A publisher that uses HTTP POST requests as the source.
 */
public final class HttpServerPublisher extends Publisher implements Runnable {
    private static final Logger LOG = LogManager.getLogger(HttpServerPublisher.class);

    private final Undertow server;
    private final PublisherOptions options;

    public HttpServerPublisher(PublisherOptions options) {
        super(options);
        this.options = options;
        if (options.getKeystore().isPresent()) {
            server = Undertow.builder().addHttpsListener(options.getPort(), "0.0.0.0", sslContext(), handler())
                    .setWorkerThreads(10).build();
        } else {
            server = Undertow.builder().addHttpListener(options.getPort(), "0.0.0.0", handler())
                    .setWorkerThreads(10).build();
        }
    }

    private static final class InfoHandler implements HttpHandler {
        @Override
        public void handleRequest(HttpServerExchange exchange) throws Exception {
            exchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"), "text/plain");
            if (exchange.getRequestMethod().equalToString("GET")) {
                if (exchange.getSecurityContext() != null) {
                    LOG.info("Authenticated: {}", exchange.getSecurityContext().isAuthenticated());
                }
                exchange.setResponseCode(200);
                exchange.getResponseSender().send("Howdy!");
            } else {
                exchange.setResponseCode(400);
                exchange.getResponseSender().send("Wrong request method!");
            }
        }
    }

    private final class UpdateHandler implements HttpHandler {
        @Override
        public void handleRequest(HttpServerExchange exchange) throws Exception {
            if (exchange.isInIoThread()) {
                exchange.dispatch(this);
                return;
            }
            exchange.getResponseHeaders().put(HttpString.tryFromString("Content-Type"), "text/plain");
            if (exchange.getRequestMethod().equalToString("POST")) {
                String contentType = Optional.ofNullable(exchange.getRequestHeaders().get("Content-Type"))
                        .map(HeaderValues::getFirst).orElse(null);
                if (contentType != null && contentType.startsWith("application/json")) {
                    try {
                        exchange.startBlocking();
                        Score score = Serialization.getJsonMapper().readValue(exchange.getInputStream(),
                                Score.class);
                        outputScore(score);
                        exchange.setResponseCode(200);
                        exchange.getResponseSender().send("SUCCESS");
                    } catch (JsonParseException e) {
                        LOG.error("Couldn't parse JSON", e);
                        exchange.setResponseCode(400);
                        exchange.getResponseSender().send("Badly formatted JSON!");
                    } catch (JsonMappingException e) {
                        LOG.error("Couldn't map JSON to Score object", e);
                        exchange.setResponseCode(400);
                        exchange.getResponseSender().send("JSON is not a valid Score");
                    } catch (IOException e) {
                        LOG.error("Unknown IO exception", e);
                        exchange.setResponseCode(400);
                        exchange.getResponseSender().send("Unknown IO error");
                    }
                } else {
                    exchange.setResponseCode(400);
                    exchange.getResponseSender().send("JSON content type required!");
                }
            } else {
                exchange.setResponseCode(400);
                exchange.getResponseSender().send("Wrong request method!");
            }
        }
    }

    private SSLContext sslContext() {
        try {
            KeyStore cks = KeyStore.getInstance(KeyStore.getDefaultType());
            cks.load(new FileInputStream(options.getKeystore().get().toFile()),
                    options.getKeystorePass().toCharArray());
            SSLContextBuilder builder = SSLContexts.custom();
            if (options.getTruststore().isPresent()) {
                KeyStore tks = KeyStore.getInstance(KeyStore.getDefaultType());
                tks.load(new FileInputStream(options.getTruststore().get().toFile()),
                        options.getTruststorePass().toCharArray());
                builder.loadTrustMaterial(tks, new TrustSelfSignedStrategy());
            }
            return builder.loadKeyMaterial(cks, options.getKeystorePass().toCharArray()).build();
        } catch (Exception e) {
            // TODO: DO SOMETHING WITH THE EXCEPTION!
            LOG.error("Exception", e);
        }
        return null;
    }

    private static IdentityManager identityManager = new IdentityManager() {
        private final Principal principal = () -> "ROOT";

        @Override
        public Account verify(Account account) {
            return null;
        }

        @Override
        public Account verify(String id, Credential credential) {
            return null;
        }

        @Override
        public Account verify(Credential credential) {
            return new Account() {
                @Override
                public Principal getPrincipal() {
                    return principal;
                }

                @Override
                public Set<String> getRoles() {
                    return Collections.emptySet();
                }
            };
        }
    };

    private HttpHandler handler() {
        HttpHandler handler = Handlers.path().addExactPath("/", new InfoHandler()).addExactPath("/update",
                new UpdateHandler());
        if (options.getKeystore().isPresent() && options.getTruststore().isPresent()) {
            return new SecurityInitialHandler(AuthenticationMode.CONSTRAINT_DRIVEN, identityManager,
                    new AuthenticationMechanismsHandler(
                            new AuthenticationConstraintHandler(new AuthenticationCallHandler(handler)),
                            Collections.singletonList(new ClientCertAuthenticationMechanism())));
        } else {
            return handler;
        }
    }

    @Override
    public void run() {
        LOG.info("Starting Undertow!");
        Thread thread = new Thread(server::start);
        thread.start();
        try {
            while (!Thread.currentThread().isInterrupted()) {
                Thread.sleep(10L);
            }
        } catch (InterruptedException ignored) {
        }
        server.stop();
        thread.interrupt();
        try {
            thread.join();
        } catch (InterruptedException ignored) {
        }
        cleanup();
    }
}