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

dk.grinn.keycloak.admin.ApplicationConfiguration Maven / Gradle / Ivy

There is a newer version: 15.0.0.1
Show newest version
package dk.grinn.keycloak.admin;

/*-
 * #%L
 * Keycloak : Migrate : Core
 * %%
 * Copyright (C) 2019 Jonas Grann & Kim Jersin
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.net.URI;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.isRegularFile;
import static java.nio.file.Files.newBufferedReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import static java.util.Arrays.stream;
import static java.util.Collections.emptyMap;
import java.util.List;
import java.util.Map;
import static java.util.stream.Collectors.toMap;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Named;
import org.apache.commons.configuration2.CompositeConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.SystemConfiguration;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
import static org.apache.commons.io.FilenameUtils.getExtension;
import org.apache.commons.lang3.concurrent.AtomicInitializer;
import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.yaml.snakeyaml.Yaml;

/**
 * Access to various application scoped resources.
 *
 * @author @author Kim Jersin <[email protected]>
 */
@ApplicationScoped
public class ApplicationConfiguration {

    public static final String CONFIG_FILENAME = "gkcadm.conf";

    public static final String ENVIRONMENT_CONFIG_PREFIX = "GKCADM_";

    private PrintStream out;

    private AtomicInitializer> checksums;

    private AtomicInitializer config;

    public ApplicationConfiguration() {
        // Lazy init of the checksums read.
        this.checksums = new AtomicInitializer<>() {
            @Override
            protected Map initialize() throws ConcurrentException {
                try {
                    return readChecksums("META-INF/migrate-checksums.csv");
                } catch (IOException ex) {
                    throw new ConcurrentException(ex);
                }
            }
        };
        
        // Lazy configuration read
        this.config = new AtomicInitializer<>() {
            @Override
            protected CompositeConfiguration initialize() throws ConcurrentException {
                // System property or environment variable
                List list = new ArrayList<>();
                String configFiles = System.getProperty("configFiles",
                        System.getenv("GKCADM_CONFIG_FILES")
                );
                if (configFiles != null) {
                    stream(configFiles.split(","))
                            .map(URI::create)
                            .forEach(list::add);
                }
                try {
                    return getConfigurationFromFiles(list);
                } catch (ConfigurationException | IOException | org.apache.commons.configuration2.ex.ConfigurationException ex) {
                    throw new RuntimeException(ex);
                }
            }
        };
    }
    
    /**
     * The main configuration of the application.
     * 

* In order of precedence:

    *
  1. System properties *
  2. Environment variables (converted to properties format) *
  3. Config files *
* * @return * @throws org.apache.commons.lang3.concurrent.ConcurrentException */ @Produces public CompositeConfiguration getConfiguration() throws ConcurrentException { return config.get(); } @Produces @ApplicationScoped public ProgressStream getProgressStream() { return new ProgressStream() { @Override public void println(String s) { out.println(s); } @Override public ProgressStream printf(String format, Object... args) { out.printf(format, args); return this; } @Override public void println() { out.println(); } @Override public void flush() { out.flush(); } @Override public OutputStream asOutputStream() { return out; } }; } @Produces @Named("checksums") public Map getChecksums() throws ConcurrentException { return checksums.get(); } public void setOutput(PrintStream out) { this.out = out; } @PostConstruct public void init() { this.out = System.out; } private CompositeConfiguration getConfigurationFromFiles(List propertyFiles) throws ConfigurationException, IOException, org.apache.commons.configuration2.ex.ConfigurationException { // Default parameters when reading from property files DefaultListDelimiterHandler listDelimiter = new DefaultListDelimiterHandler(','); // A composite configuration which includes the java system properties CompositeConfiguration configuration = new CompositeConfiguration(); SystemConfiguration system = new SystemConfiguration(); system.setListDelimiterHandler(listDelimiter); configuration.addConfiguration(system); // Environment variables (GKCADM_xxx) PropertiesConfiguration environment = new PropertiesConfiguration(); environment.setListDelimiterHandler(listDelimiter); configuration.addConfiguration(readEnvironment(environment, ENVIRONMENT_CONFIG_PREFIX)); // Read configuration from all available file sources // NOTE: Initially the FileBasedConfigurationBuilder was used to read the // files. But that promptet some strange "unable to load class" // errors. But the non-builder method, used below, works as a charm. for (URI file : propertyFiles) { Path p = Paths.get(file.getPath()); if (isRegularFile(p)) { out.printf("Using config: %s\n", p.toAbsolutePath().normalize()); switch (getExtension(p.getFileName().toString())) { case "yaml": configuration.addConfiguration(readYaml(p, file.getQuery(), listDelimiter, ENVIRONMENT_CONFIG_PREFIX)); break; default: configuration.addConfiguration(readProperties(p, listDelimiter)); } } } // And finally the defaults PropertiesConfiguration defaults = new PropertiesConfiguration(); defaults.setListDelimiterHandler(listDelimiter); try (Reader rd = new InputStreamReader(ApplicationConfiguration.class.getResourceAsStream("default-configuration.properties"), UTF_8)) { defaults.read(rd); configuration.addConfiguration(defaults); } return configuration; } private PropertiesConfiguration readEnvironment(PropertiesConfiguration config, String prefix) { System.getenv().keySet().stream() .filter(key -> key.startsWith(prefix)) .forEach(key -> config.addProperty( key.replace('_', '.').toLowerCase(), System.getenv(key) )); return config; } private PropertiesConfiguration readProperties(Path p, DefaultListDelimiterHandler listDelimiter) throws IOException, org.apache.commons.configuration2.ex.ConfigurationException { try (Reader rd = newBufferedReader(p, UTF_8)) { return readProperties(rd, listDelimiter); } } private PropertiesConfiguration readProperties(Reader rd, DefaultListDelimiterHandler listDelimiter) throws IOException, org.apache.commons.configuration2.ex.ConfigurationException { PropertiesConfiguration fileBased = new PropertiesConfiguration(); fileBased.setListDelimiterHandler(listDelimiter); fileBased.read(rd); return fileBased; } private PropertiesConfiguration readYaml(Path p, String query, DefaultListDelimiterHandler listDelimiter, String prefix) throws IOException, ConfigurationException { PropertiesConfiguration fileBased = new PropertiesConfiguration(); fileBased.setListDelimiterHandler(listDelimiter); Yaml yaml = new Yaml(); Map entries; try (Reader rd = newBufferedReader(p, UTF_8)) { Map doc = yaml.load(rd); Path yp = Paths.get(query); for (int i = 0; i < (yp.getNameCount() - 1); i++) { doc = (Map) doc.get(yp.getName(i).toString()); } entries = (Map) doc.get(yp.getFileName().toString()); } for (Map.Entry entry : entries.entrySet()) { if (entry.getKey().startsWith(prefix)) { fileBased.addProperty( entry.getKey().replace('_', '.').toLowerCase(), entry.getValue() ); } } return fileBased; } private Map readChecksums(String filename) throws IOException { try (var is = ApplicationConfiguration.class.getResourceAsStream("/" + filename)) { if (is != null) { try (var rd = new BufferedReader(new InputStreamReader(is, UTF_8))) { return rd.lines() .filter(ln -> !ln.startsWith("#")) .map(ln -> ln.split(",")) .collect(toMap( row -> row[0], row -> Long.valueOf(row[1]) )); } } else { getProgressStream().println("WARN: Checksum file not found. It is strongly advisable to include migration checksums."); } } return emptyMap(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy