public static Builder fromProperties(Properties properties)
                throws URISyntaxException {
            var builder = new Builder();

            String endpoint = properties.getProperty(
                    S3ProxyConstants.PROPERTY_ENDPOINT);
            String secureEndpoint = properties.getProperty(
                    S3ProxyConstants.PROPERTY_SECURE_ENDPOINT);
            boolean hasEndpoint = !Strings.isNullOrEmpty(endpoint);
            boolean hasSecureEndpoint = !Strings.isNullOrEmpty(secureEndpoint);
            if (!hasEndpoint && !hasSecureEndpoint) {
                throw new IllegalArgumentException(
                        "Properties file must contain: " +
                        S3ProxyConstants.PROPERTY_ENDPOINT + " or " +
                        S3ProxyConstants.PROPERTY_SECURE_ENDPOINT);
            }
            if (hasEndpoint) {
                builder.endpoint(new URI(endpoint));
            }
            if (hasSecureEndpoint) {
                builder.secureEndpoint(new URI(secureEndpoint));
            }

            String authorizationString = properties.getProperty(
                    S3ProxyConstants.PROPERTY_AUTHORIZATION);
            if (authorizationString == null) {
                throw new IllegalArgumentException(
                        "Properties file must contain: " +
                        S3ProxyConstants.PROPERTY_AUTHORIZATION);
            }

            AuthenticationType authorization =
                    AuthenticationType.fromString(authorizationString);
            String localIdentity = null;
            String localCredential = null;
            switch (authorization) {
            case AWS_V2:
            case AWS_V4:
            case AWS_V2_OR_V4:
                localIdentity = properties.getProperty(
                        S3ProxyConstants.PROPERTY_IDENTITY);
                localCredential = properties.getProperty(
                        S3ProxyConstants.PROPERTY_CREDENTIAL);
                if (localIdentity == null || localCredential == null) {
                    throw new IllegalArgumentException("Must specify both " +
                            S3ProxyConstants.PROPERTY_IDENTITY + " and " +
                            S3ProxyConstants.PROPERTY_CREDENTIAL +
                            " when using authentication");
                }
                break;
            case NONE:
                break;
            default:
                throw new IllegalArgumentException(
                        S3ProxyConstants.PROPERTY_AUTHORIZATION +
                        " invalid value, was: " + authorization);
            }

            if (localIdentity != null || localCredential != null) {
                builder.awsAuthentication(authorization, localIdentity,
                        localCredential);
            }

            String servicePath = Strings.nullToEmpty(properties.getProperty(
                    S3ProxyConstants.PROPERTY_SERVICE_PATH));
            if (servicePath != null) {
                builder.servicePath(servicePath);
            }

            String keyStorePath = properties.getProperty(
                    S3ProxyConstants.PROPERTY_KEYSTORE_PATH);
            String keyStorePassword = properties.getProperty(
                    S3ProxyConstants.PROPERTY_KEYSTORE_PASSWORD);
            if (keyStorePath != null || keyStorePassword != null) {
                builder.keyStore(keyStorePath, keyStorePassword);
            }

            String virtualHost = properties.getProperty(
                    S3ProxyConstants.PROPERTY_VIRTUAL_HOST);
            if (!Strings.isNullOrEmpty(virtualHost)) {
                builder.virtualHost(virtualHost);
            }

            String maxSinglePartObjectSize = properties.getProperty(
                    S3ProxyConstants.PROPERTY_MAX_SINGLE_PART_OBJECT_SIZE);
            if (maxSinglePartObjectSize != null) {
                builder.maxSinglePartObjectSize(Long.parseLong(
                        maxSinglePartObjectSize));
            }

            String v4MaxNonChunkedRequestSize = properties.getProperty(
                    S3ProxyConstants.PROPERTY_V4_MAX_NON_CHUNKED_REQUEST_SIZE);
            if (v4MaxNonChunkedRequestSize != null) {
                builder.v4MaxNonChunkedRequestSize(Long.parseLong(
                        v4MaxNonChunkedRequestSize));
            }

            String ignoreUnknownHeaders = properties.getProperty(
                    S3ProxyConstants.PROPERTY_IGNORE_UNKNOWN_HEADERS);
            if (!Strings.isNullOrEmpty(ignoreUnknownHeaders)) {
                builder.ignoreUnknownHeaders(Boolean.parseBoolean(
                        ignoreUnknownHeaders));
            }

            String corsAllowAll = properties.getProperty(
                    S3ProxyConstants.PROPERTY_CORS_ALLOW_ALL);
            if (!Strings.isNullOrEmpty(corsAllowAll) && Boolean.parseBoolean(
                         corsAllowAll)) {
                builder.corsRules(new CrossOriginResourceSharing());
            } else {
                String corsAllowOrigins = properties.getProperty(
                        S3ProxyConstants.PROPERTY_CORS_ALLOW_ORIGINS, "");
                String corsAllowMethods = properties.getProperty(
                        S3ProxyConstants.PROPERTY_CORS_ALLOW_METHODS, "");
                String corsAllowHeaders = properties.getProperty(
                        S3ProxyConstants.PROPERTY_CORS_ALLOW_HEADERS, "");
                String corsExposedHeaders = properties.getProperty(
                        S3ProxyConstants.PROPERTY_CORS_EXPOSED_HEADERS, "");
                String allowCredentials = properties.getProperty(
                        S3ProxyConstants.PROPERTY_CORS_ALLOW_CREDENTIAL, "");

                Splitter splitter = Splitter.on(" ").trimResults()
                        .omitEmptyStrings();

                //Validate configured methods
                Collection<String> allowedMethods = Lists.newArrayList(
                        splitter.split(corsAllowMethods));
                allowedMethods.removeAll(
                        CrossOriginResourceSharing.SUPPORTED_METHODS);
                if (!allowedMethods.isEmpty()) {
                    throw new IllegalArgumentException(
                        S3ProxyConstants.PROPERTY_CORS_ALLOW_METHODS +
                        " contains not supported values: " + Joiner.on(" ")
                        .join(allowedMethods));
                }

                builder.corsRules(new CrossOriginResourceSharing(
                        splitter.splitToList(corsAllowOrigins),
                        splitter.splitToList(corsAllowMethods),
                        splitter.splitToList(corsAllowHeaders),
                        splitter.splitToList(corsExposedHeaders),
                        allowCredentials));
            }

            String jettyMaxThreads = properties.getProperty(
                    S3ProxyConstants.PROPERTY_JETTY_MAX_THREADS);
            if (jettyMaxThreads != null) {
                builder.jettyMaxThreads(Integer.parseInt(jettyMaxThreads));
            }

            String maximumTimeSkew = properties.getProperty(
                    S3ProxyConstants.PROPERTY_MAXIMUM_TIME_SKEW);
            if (maximumTimeSkew != null && !maximumTimeSkew.trim().isEmpty()) {
                builder.maximumTimeSkew(Integer.parseInt(maximumTimeSkew));
            }

            return builder;
        }
S3Proxy translates S3 HTTP operations into jclouds provider-agnostic
operations.  This allows applications using the S3 API to interface with any
provider that jclouds supports, e.g., EMC Atmos, Microsoft Azure,
OpenStack Swift.
src/main/java/org/gaul/s3proxy/S3Proxy.java