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

org.glassfish.jersey.server.JerseySeBootstrapConfiguration Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.9
Show newest version
/*
 * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.server;

import jakarta.ws.rs.SeBootstrap;
import jakarta.ws.rs.core.UriBuilder;
import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory;
import org.glassfish.jersey.internal.config.SystemPropertiesConfigurationModel;
import org.glassfish.jersey.internal.util.PropertiesClass;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.jersey.server.spi.Container;
import org.glassfish.jersey.server.spi.WebServer;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.function.BiFunction;
import java.util.logging.Logger;

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;

/**
 * Jersey implementation of {@link SeBootstrap.Configuration} implementing arbitrary methods for acquiring
 * the configuration settings.
 * @since 3.1.0
 */
public final class JerseySeBootstrapConfiguration implements SeBootstrap.Configuration {
    private static final Logger LOGGER = Logger.getLogger(JerseySeBootstrapConfiguration.class.getName());
    protected static final Random RANDOM = new Random();
    private final SeBootstrap.Configuration configuration;

    private JerseySeBootstrapConfiguration(SeBootstrap.Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public Object property(String name) {
        return configuration.property(name);
    }

    /**
     * Compose {@link URI} based on properties defined in this configuration.
     * @param resolveDefaultPort if {@code true} the port is not set, it is resolved as
     *                           {@link Container#DEFAULT_HTTP_PORT} or {@link Container#DEFAULT_HTTPS_PORT}
     *                           based on the protocol scheme.
     * @return Composed {@link URI} based on properties defined in this configuration.
     */
    public URI uri(boolean resolveDefaultPort) {
        final String protocol = configuration.protocol();
        final String host = configuration.host();
        final int port = resolveDefaultPort ? resolvePort() : configuration.port();
        final String rootPath = configuration.rootPath();
        final URI uri = UriBuilder.newInstance().scheme(protocol.toLowerCase()).host(host).port(port).path(rootPath)
                .build();
        return uri;
    }

    private int resolvePort() {
        final int configPort = configuration.port();
        final int basePort = allowPrivilegedPorts() ? 0 : 8000;
        final int port;
        switch (configPort) {
            case SeBootstrap.Configuration.DEFAULT_PORT:
                port = basePort + (isHttps() ? Container.DEFAULT_HTTPS_PORT : Container.DEFAULT_HTTP_PORT);
                break;
            case SeBootstrap.Configuration.FREE_PORT:
               port = _resolvePort(basePort == 0);
               break;
            default:
                port = configPort;
                break;
        }
        return port;
    }

    private int _resolvePort(boolean allowPrivilegedPort) {
        final int basePort = allowPrivilegedPort ? 0 : 1024;
        // Get the initial range parameters
        final int lower = basePort;
        final int range = 0xFFFF;

        // Select a start point in the range
        final int initialOffset = RANDOM.nextInt(range - lower);

        // Loop the offset through all ports in the range and attempt
        // to bind to each
        int offset = initialOffset;
        ServerSocket socket;
        do {
            final int port = lower + offset;
            try {
                socket = new ServerSocket(port);
                socket.close();
                return port;
            } catch (IOException caught) {
                // Swallow exceptions until the end
            }
            offset = (offset + 1) % range;
        } while (offset != initialOffset);

        // If a port can't be bound, throw the exception
        throw new IllegalArgumentException(LocalizationMessages.COULD_NOT_BIND_TO_ANY_PORT());
    }

    /**
     * Return {@link SSLContext} in the configuration if the protocol scheme is {@code HTTPS}.
     * @return the SSLContext in the configuration.
     */
    @Override
    public SSLContext sslContext() {
        final SSLContext sslContext = configuration.sslContext();
        return isHttps() ? sslContext : null;
    }

    /**
     * If the protocol schema is {@code HTTPS}, return {@code true}.
     * @return {@code true} when the protocol schema is {@code HTTPS}.
     */
    public boolean isHttps() {
        return "HTTPS".equalsIgnoreCase(configuration.protocol());
    }

    /**
     * Defines if the {@link WebServer} should automatically start.
     * @return false if {@link ServerProperties#WEBSERVER_AUTO_START} is {@code false}, {@code true} otherwise.
     */
    public boolean autoStart() {
        final boolean autoStart = Optional.ofNullable(
                (Boolean) configuration.property(ServerProperties.WEBSERVER_AUTO_START))
                .orElse(TRUE);
        return autoStart;
    }

    /**
     * Defines if the {@link WebServer} should start on a privileged port when port is not set.
     * @return true if {@link ServerProperties#WEBSERVER_AUTO_START} is {@code true}, {@code false} otherwise.
     */
    public boolean allowPrivilegedPorts() {
        return Optional.ofNullable(
                (Boolean) configuration.property(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS))
                .orElse(FALSE);
    }

    /**
     * Factory method creating {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}.
     * @param configuration wrapped configuration
     * @return {@code JerseySeBootstrapConfiguration} wrapper around {@link SeBootstrap.Configuration}.
     */
    public static JerseySeBootstrapConfiguration from(SeBootstrap.Configuration configuration) {
        return JerseySeBootstrapConfiguration.class.isInstance(configuration)
                ? (JerseySeBootstrapConfiguration) configuration
                : new JerseySeBootstrapConfiguration(configuration);
    }

    /**
     * Return a Jersey instance of {@link SeBootstrap.Configuration.Builder} with prefilled values.
     * @return a Jersey instance of {@link SeBootstrap.Configuration.Builder}.
     */
    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder implements SeBootstrap.Configuration.Builder {
        private static final Map> PROPERTY_TYPES = new HashMap<>();

        static {
            PROPERTY_TYPES.put(SeBootstrap.Configuration.PROTOCOL, String.class);
            PROPERTY_TYPES.put(SeBootstrap.Configuration.HOST, String.class);
            PROPERTY_TYPES.put(SeBootstrap.Configuration.PORT, Integer.class);
            PROPERTY_TYPES.put(SeBootstrap.Configuration.ROOT_PATH, String.class);
            PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CONTEXT, SSLContext.class);
            PROPERTY_TYPES.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION, SSLClientAuthentication.class);
            PROPERTY_TYPES.put(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, Boolean.class);
            PROPERTY_TYPES.put(ServerProperties.WEBSERVER_AUTO_START, Boolean.class);
            PROPERTY_TYPES.put(ServerProperties.WEBSERVER_CLASS, Class.class);
        }

        private final Map properties = new HashMap<>();

        private Builder() {
            this.properties.put(SeBootstrap.Configuration.PROTOCOL, "HTTP"); // upper case mandated by javadoc
            this.properties.put(SeBootstrap.Configuration.HOST, "localhost");
            this.properties.put(SeBootstrap.Configuration.PORT, -1); // Auto-select port 8080 for HTTP or 8443 for HTTPS
            this.properties.put(SeBootstrap.Configuration.ROOT_PATH, "/");
            this.properties.put(ServerProperties.WEBSERVER_CLASS, WebServer.class); // Auto-select first provider
            try {
                this.properties.put(SeBootstrap.Configuration.SSL_CONTEXT, SSLContext.getDefault());
            } catch (final NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }
            this.properties.put(SeBootstrap.Configuration.SSL_CLIENT_AUTHENTICATION,
                    SeBootstrap.Configuration.SSLClientAuthentication.NONE);
            this.properties.put(ServerProperties.WEBSERVER_AUTO_START, TRUE);
            this.properties.put(ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS, FALSE);

            SystemPropertiesConfigurationModel propertiesConfigurationModel = new SystemPropertiesConfigurationModel(
                    Collections.singletonList(Properties.class.getName())
            );
            from((name, aClass) -> String.class.equals(aClass) || Integer.class.equals(aClass) || Boolean.class.equals(aClass)
                    ? propertiesConfigurationModel.getOptionalProperty(name, aClass)
                    : Optional.empty()
            );
        }

        @Override
        public JerseySeBootstrapConfiguration build() {
            return JerseySeBootstrapConfiguration.from(this.properties::get);
        }

        @Override
        public Builder property(String name, Object value) {
            this.properties.put(name, value);
            return this;
        }

        /**
         * Set the the respective {@link WebServer} class to be used by the
         * {@link org.glassfish.jersey.server.spi.WebServerProvider}.
         * @param webServerClass the class implementing {@link WebServer}.
         * @return the updated builder.
         */
        public Builder webServerClass(Class webServerClass) {
            return property(ServerProperties.WEBSERVER_CLASS, webServerClass);
        }

        /**
         * Define if the {@link WebServer} should auto-start at bootstrap.
         * @param autostart the auto-start flag.
         * @return the updated builder.
         */
        public Builder autoStart(Boolean autostart) {
            return property(ServerProperties.WEBSERVER_AUTO_START, autostart);
        }

        @Override
        public  JerseySeBootstrapConfiguration.Builder from(BiFunction, Optional> configProvider) {
            PROPERTY_TYPES.forEach(
                    (propertyName, propertyType) -> configProvider.apply(propertyName, (Class) propertyType)
                            .ifPresent(propertyValue -> this.properties.put(propertyName, propertyValue)));
            return this;
        }

        @Override
        public JerseySeBootstrapConfiguration.Builder from(Object externalConfig) {
            if (SeBootstrap.Configuration.class.isInstance(externalConfig)) {
                final SeBootstrap.Configuration other = (SeBootstrap.Configuration) externalConfig;
                from((name, clazz) -> {
                    final Object property = other.property(name);
                    if (property != null) {
                        if (clazz.equals(property.getClass())) {
                            return Optional.of(property);
                        } else {
                            LOGGER.warning(LocalizationMessages.IGNORE_SEBOOTSTRAP_CONFIGURATION_PROPERTY(name, clazz));
                        }
                    }
                    return Optional.empty();
                });
            }
            return this;
        }
    }

    /**
     * Name the properties to be internally read from System properties by {@link ExternalPropertiesConfigurationFactory}.
     * This is required just when SecurityManager is on, otherwise all system properties are read.
     */
    @PropertiesClass
    private static class Properties {
        /**
         * See {@link SeBootstrap.Configuration#PROTOCOL} property.
         */
        public static final String SE_BOOTSTRAP_CONFIGURATION_PROTOCOL = SeBootstrap.Configuration.PROTOCOL;

        /**
         * See {@link SeBootstrap.Configuration#HOST} property.
         */
        public static final String SE_BOOTSTRAP_CONFIGURATION_HOST = SeBootstrap.Configuration.HOST;

        /**
         * See {@link SeBootstrap.Configuration#PORT} property.
         */
        public static final String SE_BOOTSTRAP_CONFIGURATION_PORT = SeBootstrap.Configuration.PORT;

        /**
         * See {@link SeBootstrap.Configuration#ROOT_PATH} property.
         */
        public static final String SE_BOOTSTRAP_CONFIGURATION_ROOT_PATH = SeBootstrap.Configuration.ROOT_PATH;

        /**
         * See {@link ServerProperties#WEBSERVER_ALLOW_PRIVILEGED_PORTS} property.
         */
        public static final String WEBSERVER_ALLOW_PRIVILEGED_PORTS  = ServerProperties.WEBSERVER_ALLOW_PRIVILEGED_PORTS;

        /**
         * See {@link ServerProperties#WEBSERVER_AUTO_START} property.
         */
        public static final String WEBSERVER_AUTO_START = ServerProperties.WEBSERVER_AUTO_START;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy