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

com.github.cafapi.common.config.sources.file.FileConfigurationSource Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015-2024 Open Text.
 *
 * 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 com.github.cafapi.common.config.sources.file;

import com.github.cafapi.common.api.BootstrapConfiguration;
import com.github.cafapi.common.config.source.CafConfigurationSource;
import com.github.cafapi.common.api.Cipher;
import com.github.cafapi.common.api.ConfigurationException;
import com.github.cafapi.common.api.Decoder;
import com.github.cafapi.common.api.FileExtensions;
import com.github.cafapi.common.api.HealthResult;
import com.github.cafapi.common.util.naming.Name;
import com.github.cafapi.common.util.naming.ServicePath;
import java.io.Closeable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;

/**
 * This is a ConfigurationProvider that reads from a local disk file. If the bootstrap parameter config.path is set, it will read the
 * config files from the specified directory, otherwise it will use the working dir.
 *
 * If retrieving the configuration class "TestConfiguration", using the ServicePath /a/b, it will expect the file to be called
 * "cfg_a_b_TestConfiguration".
 */
public class FileConfigurationSource extends CafConfigurationSource
{
    public static final String CONFIG_PATH = "CAF_CONFIG_PATH";
    public static final String RESOURCE_PATH = "CAF_RESOURCE_PATH";
    @Deprecated
    public static final String OLD_CONFIG_PATH = "config.path";
    private final FileSystem configFilesystem;
    private final Path configPath;
    private static final Logger LOG = LoggerFactory.getLogger(FileConfigurationSource.class);
    private static final ArrayList fileNameDelimiters = new ArrayList<>();
    private final String[] fileExtensions;

    static {
        fileNameDelimiters.add("_");
        fileNameDelimiters.add("~");
    }

    /**
     * {@inheritDoc}
     */
    public FileConfigurationSource(
        final BootstrapConfiguration bootstrap,
        final Cipher cipher,
        final ServicePath servicePath,
        final Decoder decoder
    ) throws ConfigurationException
    {
        super(bootstrap, cipher, servicePath, decoder);
        try {
            /**
             * This method was identified as a possible issue for path manipulation, marking as false positive due to the path being
             * picked up from environment variable. Creating a regex to match valid paths here would be overkill.
             */
            if (bootstrap.isConfigurationPresent(CONFIG_PATH)) {
                configFilesystem = null;
                configPath = FileSystems.getDefault().getPath(bootstrap.getConfiguration(CONFIG_PATH));
            } else if (bootstrap.isConfigurationPresent(OLD_CONFIG_PATH)) {
                configFilesystem = null;
                configPath = FileSystems.getDefault().getPath(bootstrap.getConfiguration(OLD_CONFIG_PATH));
            } else if (bootstrap.isConfigurationPresent(RESOURCE_PATH)) {
                final URL resourceUrl = Thread.currentThread().getContextClassLoader()
                    .getResource(bootstrap.getConfiguration(RESOURCE_PATH));
                if (resourceUrl == null) {
                    throw new ConfigurationException("Resource path " + RESOURCE_PATH + " not found");
                }

                final URI resourceUri = resourceUrl.toURI();

                if (resourceUri.getScheme().equalsIgnoreCase("file")) {
                    configFilesystem = null;
                    configPath = FileSystems.getDefault().provider().getPath(resourceUri);
                } else {
                    try {
                        configFilesystem = FileSystems.newFileSystem(resourceUri, Collections.emptyMap());
                    } catch (final IOException ex) {
                        throw new ConfigurationException("I/O error creating resource file system", ex);
                    }

                    try {
                        configPath = configFilesystem.provider().getPath(resourceUri);
                    } catch (final Exception ex) {
                        closeSilently(configFilesystem);
                        throw ex;
                    }
                }
            } else {
                throw new ConfigurationException("Configuration parameter " + CONFIG_PATH + " not present");
            }
        } catch (final InvalidPathException | URISyntaxException e) {
            throw new ConfigurationException("Invalid configuration path", e);
        }
        fileExtensions = getFileExtensions(decoder);
        LOG.debug("Initialised");
    }

    @Override
    public void shutdown()
    {
        closeSilently(configFilesystem);
    }

    @Override
    public HealthResult healthCheck()
    {
        return HealthResult.RESULT_HEALTHY;
    }

    @Override
    protected InputStream getConfigurationStream(final Class configClass, final Name relativePath)
        throws ConfigurationException
    {
        // Try each configuration source filename format delimiter in attempt to load the configuration source
        for (final String fileNameDelimiter : fileNameDelimiters) {
            for (final String fileExtension : fileExtensions) {
                String configFile = nameToFile(configClass, relativePath, fileNameDelimiter, fileExtension);
                Path p;
                if (configPath != null) {
                    p = configPath.resolve(configFile);
                } else {
                    p = Paths.get(configFile);
                }
                LOG.debug("Getting configuration for {} from {}", configClass.getSimpleName(), p);
                // Check if the file exists and try to return it as an input stream
                if (Files.exists(p)) {
                    try {
                        return Files.newInputStream(p);
                    } catch (final IOException ioe) {
                        throw new ConfigurationException("Cannot read config file: " + configFile, ioe);
                    }
                }
            }
        }
        throw new ConfigurationException("Cannot find config file for " + configClass.getSimpleName());
    }

    /**
     * Retrieves the file extensions to use with the specified decoder.
     * 

* If the specified Decoder doesn't have any file extensions explicitly associated with it, then a single empty string is returned. */ private static String[] getFileExtensions(final Decoder decoder) { final Class decoderClass = decoder.getClass(); final FileExtensions fileExtensionsAnnotation = decoderClass.getAnnotation(FileExtensions.class); return (fileExtensionsAnnotation == null) ? new String[]{""} : fileExtensionsAnnotation.value(); } /** * Convert a (partial) ServicePath into a file name to access. The file names are in the format * "cfg_group_subgroup_appid_ConfigurationClass[.ext]". * * @param configClass the configuration class to try and acquire * @param servicePath the partial or complete ServicePath in Name format * @param fileNameDelimiter the symbol used to separate cfg, group, subgroup, appid and ConfigurationClass * @param extension the file extension, or an empty string if there is no extension * @return the constructed file name to try and access */ private String nameToFile(final Class configClass, final Name servicePath, final String fileNameDelimiter, final String extension) { StringBuilder builder = new StringBuilder("cfg"); for (final String component : servicePath) { builder.append(fileNameDelimiter).append(component); } builder.append(fileNameDelimiter).append(configClass.getSimpleName()); if (!extension.isEmpty()) { builder.append('.').append(extension); } return builder.toString(); } private static void closeSilently(final Closeable closee) { if (closee != null) { try { closee.close(); } catch (final IOException ex) { LOG.error("Error releasing system resources", ex); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy