All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ninja.standalone.AbstractStandalone Maven / Gradle / Ivy

/**
 * Copyright (C) the original author or authors.
 *
 * Licensed 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 ninja.standalone;

import static ninja.standalone.StandaloneHelper.checkContextPath;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import ninja.utils.NinjaConstant;
import ninja.utils.NinjaMode;
import org.apache.commons.lang3.StringUtils;
import javax.net.ssl.SSLContext;

/**
 * Abstract Standalone that implements most functionality required to write
 * a concrete Standalone.  Introduces new doStart(), doStop(), and doJoin()
 * methods which are actually where you'll place most of your logic.  You'll
 * also want to subclass the configure() method to add any configuration
 * specific to your Standalone.  See NinjaJetty for example concrete implementation.
 * @param  The concrete standalone implementation
 */
abstract public class AbstractStandalone extends AbstractConsole implements Standalone, Runnable {
    
    // can all be changed prior to configure()
    protected String host;
    protected Integer port;
    protected String contextPath;
    protected Long idleTimeout;
    protected Integer sslPort;
    protected URI sslKeystoreUri;
    protected String sslKeystorePassword;
    protected URI sslTruststoreUri;
    protected String sslTruststorePassword;
    // internal state
    protected List serverUrls;
    protected List baseUrls;
    
    public AbstractStandalone(String name) {
        super(name);
    }
    
    @Override
    protected void doPreConfigure() throws Exception {
        // current value or system property or conf/application.conf or default value
        host(overlayedNinjaProperties.get(Standalone.KEY_NINJA_HOST, this.host, Standalone.DEFAULT_HOST));
        port(overlayedNinjaProperties.getInteger(Standalone.KEY_NINJA_PORT, this.port, Standalone.DEFAULT_PORT));
        contextPath(overlayedNinjaProperties.get(Standalone.KEY_NINJA_CONTEXT_PATH, this.contextPath, Standalone.DEFAULT_CONTEXT_PATH));
        idleTimeout(overlayedNinjaProperties.getLong(Standalone.KEY_NINJA_IDLE_TIMEOUT, this.idleTimeout, Standalone.DEFAULT_IDLE_TIMEOUT));
        sslPort(overlayedNinjaProperties.getInteger(Standalone.KEY_NINJA_SSL_PORT, this.sslPort, Standalone.DEFAULT_SSL_PORT));
        // defaults below (with self-signed cert) only valid in dev & test modes
        sslKeystoreUri(overlayedNinjaProperties.getURI(Standalone.KEY_NINJA_SSL_KEYSTORE_URI, this.sslKeystoreUri, this.ninjaMode == NinjaMode.prod ? null : new URI(Standalone.DEFAULT_DEV_NINJA_SSL_KEYSTORE_URI)));
        sslKeystorePassword(overlayedNinjaProperties.get(Standalone.KEY_NINJA_SSL_KEYSTORE_PASSWORD, this.sslKeystorePassword, this.ninjaMode == NinjaMode.prod ? null : Standalone.DEFAULT_DEV_NINJA_SSL_KEYSTORE_PASSWORD));
        sslTruststoreUri(overlayedNinjaProperties.getURI(Standalone.KEY_NINJA_SSL_TRUSTSTORE_URI, this.sslTruststoreUri, this.ninjaMode == NinjaMode.prod ? null : new URI(Standalone.DEFAULT_DEV_NINJA_SSL_TRUSTSTORE_URI)));
        sslTruststorePassword(overlayedNinjaProperties.get(Standalone.KEY_NINJA_SSL_TRUSTSTORE_PASSWORD, this.sslTruststorePassword, this.ninjaMode == NinjaMode.prod ? null : Standalone.DEFAULT_DEV_NINJA_SSL_TRUSTSTORE_PASSWORD));
        // assign random ports if needed
        if (getPort() == null || getPort() == 0) {
            port(StandaloneHelper.findAvailablePort(8000, 9000));
        }
        if (getSslPort() == null || getSslPort() == 0) {
            sslPort(StandaloneHelper.findAvailablePort(9001, 9999));
        }
    }
    
    @Override
    protected void doPostConfigure() throws Exception {
        // build configured urls
        this.serverUrls = createServerUrls();
        this.baseUrls = createBaseUrls();
        // is there at least one url?
        if (this.serverUrls == null || this.serverUrls.isEmpty()) {
            throw new IllegalStateException("All server ports were disabled." + " Check the 'ninja.port' property and possibly others depending your standalone.");
        }
        // save generated server name as ninja property if its not yet set
        String serverName = this.ninjaProperties.get(NinjaConstant.serverName);
        if (StringUtils.isEmpty(serverName)) {
            // grab the first one
            this.ninjaProperties.setProperty(NinjaConstant.serverName, getServerUrls().get(0));
        }
    }
    
    /**
     * Configure, start, add shutdown hook, and join.  Does not exit.
     */
    @Override
    final public void run() {
        // name current thread for improved logging/debugging
        Thread.currentThread().setName(this.name);
        
        try {
            this.configure();
        } catch (Exception e) {
            logger.error("Unable to configure {}", name, e);
            System.exit(1);
        }
 
        try {
            this.start();
        } catch (Exception e) {
            logger.error("Unable to start {}", name, e);
            System.exit(1);
        }
        
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                shutdown();
            }
        });
        
        try {
            // do not simply exit main() -- join something (likely server)
            join();
        } catch (Exception e) {
            logger.warn("Interrupted (most likely JVM is shutting down and this is safe to ignore)");
        }
    }
    
    @Override
    final public T join() throws Exception{
        checkStarted();
        
        doJoin();
        
        return (T)this;
    }
    
    abstract protected void doJoin() throws Exception;
    
    @Override
    public Integer getPort() {
        return this.port;
    }
    
    @Override
    public T port(int port) {
        this.port = port;
        return (T)this;
    }

    @Override
    public String getHost() {
        return this.host;
    }
    
    @Override
    public T host(String host) {
        this.host = host;
        return (T)this;
    }
    
    @Override
    public Long getIdleTimeout() {
        return idleTimeout;
    }
    
    @Override
    public T idleTimeout(long idleTimeout) {
        this.idleTimeout = idleTimeout;
        return (T)this;
    }

    @Override
    public String getContextPath() {
        return contextPath;
    }
    
    @Override
    public T contextPath(String contextPath) {
        checkContextPath(contextPath);
        this.contextPath = contextPath;
        return (T)this;
    }
    
    @Override
    public Integer getSslPort() {
        return this.sslPort;
    }
    
    @Override
    public T sslPort(int sslPort) {
        this.sslPort = sslPort;
        return (T)this;
    }

    @Override
    public URI getSslKeystoreUri() {
        return this.sslKeystoreUri;
    }

    @Override
    public T sslKeystoreUri(URI keystoreUri) {
        this.sslKeystoreUri = keystoreUri;
        return (T)this;
    }

    @Override
    public String getSslKeystorePassword() {
        return this.sslKeystorePassword;
    }

    @Override
    public T sslKeystorePassword(String keystorePassword) {
        this.sslKeystorePassword = keystorePassword;
        return (T)this;
    }

    @Override
    public URI getSslTruststoreUri() {
        return this.sslTruststoreUri;
    }

    @Override
    public T sslTruststoreUri(URI truststoreUri) {
        this.sslTruststoreUri = truststoreUri;
        return (T)this;
    }

    @Override
    public String getSslTruststorePassword() {
        return this.sslTruststorePassword;
    }

    @Override
    public T sslTruststorePassword(String truststorePassword) {
        this.sslTruststorePassword = truststorePassword;
        return (T)this;
    }
    
    @Override
    public List getServerUrls() {
        // only available after configure()
        checkConfigured();
        return serverUrls;
    }
    
    @Override
    public List getBaseUrls() {
        // only available after configure()
        checkConfigured();
        return baseUrls;
    }
    
    @Override
    public boolean isPortEnabled() {
        return this.port != null && this.port > -1;
    }
    
    @Override
    public boolean isSslPortEnabled() {
        return this.sslPort != null && this.sslPort > -1;
    }
    
    protected List createServerUrls() {
        // only available after configure()
        checkConfigured();
        
        List urls = new ArrayList<>();
        
        if (isPortEnabled()) {
            urls.add(createServerUrl("http", getHost(), getPort()));
        }
        
        if (isSslPortEnabled()) {
            urls.add(createServerUrl("https", getHost(), getSslPort()));
        }
        
        return urls;
    }
    
    protected List createBaseUrls() {
        // only available after configure()
        checkConfigured();
        
        List urls = new ArrayList<>();
        
        if (isPortEnabled()) {
            urls.add(createBaseUrl("http", getHost(), getPort(), getContextPath()));
        }
        
        if (isSslPortEnabled()) {
            urls.add(createBaseUrl("https", getHost(), getSslPort(), getContextPath()));
        }
        
        return urls;
    }
    
    // helpful utilities for subclasses
    
    protected String createServerUrl(String scheme, String host, Integer port) {
        StringBuilder sb = new StringBuilder();
        
        sb.append(scheme);
        sb.append("://");
        sb.append((host != null ? host : "localhost"));
        
        if (("http".equals(scheme) && port != 80) || ("https".equals(scheme) && port != 443)) {
            sb.append(":");
            sb.append(port);
        }
        
        return sb.toString();
    }
    
    protected String createBaseUrl(String scheme, String host, Integer port, String context) {
        StringBuilder sb = new StringBuilder();
        
        sb.append(createServerUrl(scheme, host, port));
        
        if (StringUtils.isNotEmpty(context)) {
            sb.append(context);
        }
        
        return sb.toString();
    }
    
    
    protected String getLoggableIdentifier() {
        // build list of ports
        StringBuilder ports = new StringBuilder();
        
        if (isPortEnabled()) {
            ports.append(getPort());
        }
        
        if (isSslPortEnabled()) {
            if (ports.length() > 0) {
                ports.append(", ");
            }
            ports.append(getSslPort());
            ports.append("/ssl");
        }
        
        StringBuilder s = new StringBuilder();
        
        s.append("on ");
        
        s.append(Optional.ofNullable(getHost()).orElse(""));
        s.append(":");
        s.append(ports);

        return s.toString();
    }
    
    @Override
    protected void logStarted() {
        logger.info("-------------------------------------------------------");
        logger.info("Ninja application running at");
        
        List uris = this.getBaseUrls();
        if (uris != null) {
            uris.forEach(uri -> {
                logger.info(" => {}", uri);
            });
        }
        
        logger.info("-------------------------------------------------------");
    }
    
    protected SSLContext createSSLContext() throws Exception {
        if (this.sslKeystoreUri == null) {
            throw new IllegalStateException("Unable to create SSL context. Configuration key " + KEY_NINJA_SSL_KEYSTORE_URI
                    + " has empty value.  Please check your configuration file.");
        }
        
        if (this.sslKeystorePassword == null) {
            throw new IllegalStateException("Unable to create SSL context. Configuration key " + KEY_NINJA_SSL_KEYSTORE_PASSWORD
                    + " has empty value.  Please check your configuration file.");
        }
        
        if (this.sslTruststoreUri == null) {
            throw new IllegalStateException("Unable to create SSL context. Configuration key " + KEY_NINJA_SSL_TRUSTSTORE_URI
                    + " has empty value.  Please check your configuration file.");
        }
        
        if (this.sslTruststorePassword == null) {
            throw new IllegalStateException("Unable to create SSL context. Configuration key " + KEY_NINJA_SSL_TRUSTSTORE_PASSWORD
                    + " has empty value.  Please check your configuration file.");
        }
        
        return StandaloneHelper.createSSLContext(this.sslKeystoreUri, this.sslKeystorePassword.toCharArray(),
            this.sslTruststoreUri, this.sslTruststorePassword.toCharArray());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy