com.mchange.v3.hocon.HoconPropertiesConfigSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.servicemix.bundles.mchange-commons-java
Show all versions of org.apache.servicemix.bundles.mchange-commons-java
This OSGi bundle wraps ${pkgArtifactId} ${pkgVersion} jar file.
/*
* Distributed as part of mchange-commons-java 0.2.11
*
* Copyright (C) 2015 Machinery For Change, Inc.
*
* Author: Steve Waldman
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of EITHER:
*
* 1) The GNU Lesser General Public License (LGPL), version 2.1, as
* published by the Free Software Foundation
*
* OR
*
* 2) The Eclipse Public License (EPL), version 1.0
*
* You may choose which license to accept if you wish to redistribute
* or modify this work. You may offer derivatives of this work
* under the license you have chosen, or you may provide the same
* choice of license which you have been offered here.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received copies of both LGPL v2.1 and EPL v1.0
* along with this software; see the files LICENSE-EPL and LICENSE-LGPL.
* If not, the text of these licenses are currently available at
*
* LGPL v2.1: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* EPL v1.0: http://www.eclipse.org/org/documents/epl-v10.php
*
*/
package com.mchange.v3.hocon;
import java.util.*;
import java.io.*;
import com.typesafe.config.*;
import com.mchange.v2.cfg.*;
import com.mchange.v2.lang.SystemUtils;
import java.net.MalformedURLException;
import java.net.URL;
import static com.mchange.v3.hocon.HoconUtils.*;
import static com.mchange.v2.cfg.DelayedLogItem.*;
/**
* An implementation of {@link com.mchange.v2.cfg.PropertiesConfigSource} that reads HOCON configs
* into properties for incorporation into {@link com.mchange.v2.cfg.MultiPropertiesConfig}.
*
* HOCON config files are read as resources by an identifier, which might look like
*
* hocon:reference,application,special.json,scoped.conf#my-scope,/
*
* Elements within this path are interpreted as URLs if they contain a colon, e.g.
*
* hocon:reference,application,http://my.host.name/networkconfig,/
*
* URL elements can contain substitutions based on System properties or environment variables
* (with System properties taking preference if both are found)
*
* hocon:reference,application,file:${user.home}/.myconfig,/
*
* NOTE: Hash symbols (#) within URLs will be interpreted as denoting a config scope (see below).
* They and any subsequent text will NOT be treated as part of the URL
*
* All Configs found are merged, with later elements in the list taking preference over
* earlier elements. Substitutions within the full, merged Config are resolved.
*
* Elements that have no suffix (or, more exactly, that contain no '.' character) are read
* appending all three HOCON standard suffixes, so that "xxx" reads all three of
* xxx.conf, xxx.json, and xxx.properties.
*
* Anything that follows a '#' character in an identifier is treated as a scope,
* such that the only Config loaded (as the new top-level!) are those underneath
* the scope key. So, in JSON format, if the config under scoped.conf is
*
* {
* "some-top-level-key" : "hello",
*
* "my-scope" : {
* "a" : "apple",
* "b" : "book",
* "c" : "cat"
* }
* }
*
* scoped.conf#my-scope will be read as:
*
* {
* "a" : "apple",
* "b" : "book",
* "c" : "cat"
* }
*
* The special element '/' refers to a Config containing System properties.
*
* Following HOCON config conventions, resources under the identifier 'application'
* will be replaced if any of "config.resource", "config.file", or "config.url"
* are set.
*
*/
public final class HoconPropertiesConfigSource implements PropertiesConfigSource
{
private static Config extractConfig( ClassLoader cl, String identifier, List dlis ) throws FileNotFoundException, Exception
{
int pfx_index = identifier.indexOf(':');
List configs = new ArrayList();
if ( pfx_index >= 0 && "hocon".equals( identifier.substring(0, pfx_index).toLowerCase() ) )
{
String allFilesStr = identifier.substring( pfx_index + 1 ).trim();
String[] allFiles = allFilesStr.split("\\s*,\\s*");
for ( String file : allFiles )
{
String resourcePath;
String scopePath;
int sfx_index = file.lastIndexOf('#');
if ( sfx_index > 0 )
{
resourcePath = file.substring( 0, sfx_index );
scopePath = file.substring( sfx_index + 1 ).replace('/','.').trim();
}
else
{
resourcePath = file;
scopePath = null;
}
Config config = null;
if ( "/".equals( resourcePath ) )
config = ConfigFactory.systemProperties();
else
{
Config rawConfig = null;
// XXX: This code has been extracted into HoconUtils, but not refactored out.
// ( If there is something wrong here, don't forget to check there also
// for the same issue. )
//
// TODO: Refactor the two classes, maybe using a Callable in the main
// utility to deal with heterogeneous failure responses
if ("application".equals( resourcePath ) || "/application".equals( resourcePath ))
{
// following typesafe-config standard behavior, we override
// the standard identifier "application" if System properties
// "config.resource", "config.file", or "config.url" are set.
String check;
if ( ( check = System.getProperty("config.resource") ) != null )
resourcePath = check;
else if ( ( check = System.getProperty("config.file") ) != null )
{
File f = new File( check );
if ( f.exists() )
{
if ( f.canRead() )
rawConfig = ConfigFactory.parseFile( f );
else
dlis.add( new DelayedLogItem( Level.WARNING,
String.format("Specified config.file '%s' is not readable. Falling back to standard application.(conf|json|properties).}", f.getAbsolutePath()) ) );
}
else
dlis.add( new DelayedLogItem( Level.WARNING,
String.format("Specified config.file '%s' does not exist. Falling back to standard application.(conf|json|properties).}", f.getAbsolutePath()) ) );
}
else if ( ( check = System.getProperty("config.url") ) != null )
rawConfig = ConfigFactory.parseURL( new URL( check ) );
}
if ( rawConfig == null )
{
URL url = null;
if ( resourcePath.indexOf(":") >= 0 )
{
try
{
String substitutedPath = SystemUtils.sysPropsEnvReplace( resourcePath );
url = new URL( substitutedPath );
}
catch ( MalformedURLException e )
{
dlis.add( new DelayedLogItem( Level.WARNING, String.format("Apparent URL resource path for HOCON '%s' could not be parsed as a URL.", resourcePath), e ) );
// leave URL as null
}
}
if ( url != null )
{
rawConfig = ConfigFactory.parseURL( url );
}
else
{
if (resourcePath.charAt(0) == '/') // when loading resources from a classloader, leave out the leading slash...
resourcePath = resourcePath.substring(1);
boolean includes_suffix = (resourcePath.indexOf('.') >= 0);
if ( includes_suffix )
rawConfig = ConfigFactory.parseResources( cl, resourcePath );
else
rawConfig = ConfigFactory.parseResourcesAnySyntax( cl, resourcePath );
}
}
if ( rawConfig.isEmpty() )
dlis.add( new DelayedLogItem( Level.FINE, String.format("Missing or empty HOCON configuration for resource path '%s'.", resourcePath) ) );
else
config = rawConfig;
}
if (config != null)
{
if (scopePath != null)
config = config.getConfig( scopePath );
configs.add( config );
}
}
if ( configs.size() == 0)
throw new FileNotFoundException( String.format("Could not find HOCON configuration at any of the listed resources in '%s'", identifier) );
else
{
Config bigConfig = ConfigFactory.empty();
for (int i = configs.size(); --i >= 0; )
bigConfig = bigConfig.withFallback( configs.get(i) );
return bigConfig.resolve(); // impose intra-Config substitutions
}
}
else
throw new IllegalArgumentException( String.format("Invalid resource identifier for hocon config file: '%s'", identifier) );
}
public Parse propertiesFromSource( ClassLoader cl, String identifier ) throws FileNotFoundException, Exception
{
List dlis = new LinkedList();
Config config = extractConfig( cl, identifier, dlis );
PropertiesConversion pc = configToProperties( config );
for( String path : pc.unrenderable )
dlis.add( new DelayedLogItem( Level.FINE, String.format("Value at path '%s' could not be converted to a String. Skipping.", path) ) );
return new Parse( pc.properties, dlis );
}
public Parse propertiesFromSource( String identifier ) throws FileNotFoundException, Exception
{ return propertiesFromSource( HoconPropertiesConfigSource.class.getClassLoader(), identifier ); }
}