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

io.inverno.mod.configuration.source.CPropsFileConfigurationSource Maven / Gradle / Ivy

There is a newer version: 1.11.0
Show newest version
/*
 * Copyright 2020 Jeremy KUHN
 *
 * 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 io.inverno.mod.configuration.source;

import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import io.inverno.mod.base.converter.SplittablePrimitiveDecoder;
import io.inverno.mod.base.resource.Resource;
import io.inverno.mod.base.resource.ResourceException;
import io.inverno.mod.configuration.AbstractHashConfigurationSource;
import io.inverno.mod.configuration.ConfigurationProperty;
import io.inverno.mod.configuration.DefaultingStrategy;
import io.inverno.mod.configuration.internal.JavaStringConverter;
import io.inverno.mod.configuration.internal.parser.properties.ConfigurationPropertiesParser;
import io.inverno.mod.configuration.internal.parser.properties.ParseException;
import io.inverno.mod.configuration.internal.parser.properties.StreamProvider;
import reactor.core.publisher.Mono;

/**
 * 

* A configuration source that looks up properties in a configuration properties file following the {@code .cprops} file format. *

* *

* This source supports parameterized configuration properties defined in a configuration file as follows: *

* *
{@code
 * web {
 *     server_port=8080
 *     [ profile = "ssl" ] {
 *         server_port=8443
 *     }
 * }
 *
 * [ env="dev" ] {
 *     db.url="jdbc:oracle:thin:@dev.db.server:1521:sid"
 * }
 *
 * [ env="prod" ] {
 *     db.url="jdbc:oracle:thin:@dev.db.server:1521:sid"
 *     [ zone="eu" ] {
 *         db.url="jdbc:oracle:thin:@prod_eu.db.server:1521:sid"
 *     }
 *     [ zone="us" ] {
 *         db.url="jdbc:oracle:thin:@prod_us.db.server:1521:sid"
 *     }
 * }
 * }
* *

* Please refer to the {@code .cprops} format definition for more information on how to create parameterized configuration in the {@code .cprops} format. *

* * @author Jeremy Kuhn * @since 1.0 * * @see AbstractHashConfigurationSource */ public class CPropsFileConfigurationSource extends AbstractHashConfigurationSource { private static final Logger LOGGER = LogManager.getLogger(CPropsFileConfigurationSource.class); private Path propertyFile; private Resource propertyResource; private InputStream propertyInput; private Duration propertiesTTL; private Mono> properties; /** *

* Creates a {@code .cprops} file configuration source with the file at the specified path. *

* * @param propertyFile the path to the {@code .cprops} file */ public CPropsFileConfigurationSource(Path propertyFile) { this(propertyFile, new JavaStringConverter()); } /** *

* Creates a {@code .cprops} file configuration source with the file at the specified path and the specified string value decoder. *

* * @param propertyFile the path to the {@code .cprops} file * @param decoder a string decoder */ public CPropsFileConfigurationSource(Path propertyFile, SplittablePrimitiveDecoder decoder) { super(decoder); this.propertyFile = propertyFile; } /** *

* Creates a {@code .cprops} file configuration source with the specified input stream. *

* * @param propertyInput the {@code .cprops} input */ public CPropsFileConfigurationSource(InputStream propertyInput) { this(propertyInput, new JavaStringConverter()); } /** *

* Creates a {@code .cprops} file configuration source with the specified input stream and string value decoder. *

* * @param propertyInput the {@code .cprops} input * @param decoder a string decoder */ public CPropsFileConfigurationSource(InputStream propertyInput, SplittablePrimitiveDecoder decoder) { super(decoder); this.propertyInput = propertyInput; } /** *

* Creates a {@code .cprops} file configuration source with the specified resource *

* * @param propertyResource the {@code .properties} resource */ public CPropsFileConfigurationSource(Resource propertyResource) { this(propertyResource, new JavaStringConverter()); } /** *

* Creates a {@code .cprops} file configuration source with the specified resource and string value decoder. *

* * @param propertyResource the {@code .properties} resource * @param decoder a string decoder */ public CPropsFileConfigurationSource(Resource propertyResource, SplittablePrimitiveDecoder decoder) { super(decoder); this.propertyResource = propertyResource; } /** *

* Creates a {@code .cprops} file configuration source from the specified initial source and using the specified defaulting strategy. *

* * @param initial the initial configuration source. * @param defaultingStrategy a defaulting strategy */ private CPropsFileConfigurationSource(CPropsFileConfigurationSource initial, DefaultingStrategy defaultingStrategy) { super(initial, defaultingStrategy); this.propertyFile = initial.propertyFile; this.propertyResource = initial.propertyResource; this.propertyInput = initial.propertyInput; this.properties = initial.properties; } @Override public CPropsFileConfigurationSource withDefaultingStrategy(DefaultingStrategy defaultingStrategy) { return new CPropsFileConfigurationSource(this.initial != null ? this.initial : this, defaultingStrategy); } /** *

* Sets the time-to-live duration of the properties loaded with {@link #load()}. *

* *

* If set to null, which is the default, properties are cached indefinitely. *

* *

* Note that this ttl doesn't apply to a source created with an {@link InputStream} which is cached indefinitely since the steam can't be read twice. *

* * @param ttl the properties time-to-live or null to cache properties indefinitely */ public void setPropertiesTTL(Duration ttl) { this.propertiesTTL = ttl; this.properties = null; } /** *

* Returns the time-to-live duration of the properties loaded with {@link #load()}. *

* * @return the properties time-to-live or null if properties are cached indefinitely */ public Duration getPropertiesTTL() { return this.propertiesTTL; } /** *

* Opens an input stream to read the {@code .cprops} file. *

* * @return An input stream * * @throws IOException if there was an I/O error opening the file * @throws ResourceException if there was an error opening the resource */ private InputStream open() throws IOException, ResourceException { if(this.propertyFile != null) { return Files.newInputStream(this.propertyFile); } else if(this.propertyResource != null) { return this.propertyResource.openReadableByteChannel().map(Channels::newInputStream).orElseThrow(() -> new ResourceException("Property file " + this.propertyResource.getURI() + " is not readable")); } else { return this.propertyInput; } } @Override protected Mono> load() { if(this.properties == null) { this.properties = Mono.defer(() -> { try(InputStream input = this.open()) { ConfigurationPropertiesParser parser = new ConfigurationPropertiesParser<>(new StreamProvider(input)); parser.setConfigurationSource(this); return Mono.just(parser.StartConfigurationProperties()); } catch (IOException | ParseException | ResourceException e) { LOGGER.warn(() -> "Invalid property file: " + e.getMessage()); return Mono.error(e); } }); if(this.propertyInput != null) { this.properties = this.properties.cache(); } else if(this.propertiesTTL != null) { this.properties = this.properties.cache(this.propertiesTTL); } } return this.properties; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy