/* * 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.addthis.hydra.uber; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import com.google.common.io.ByteStreams; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigObject; import com.typesafe.config.ConfigParseOptions; import com.typesafe.config.ConfigRenderOptions; import com.typesafe.config.ConfigResolveOptions; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Order; import org.apache.logging.log4j.core.config.json.JsonConfigurationFactory; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.util.Loader; @Plugin(name = "HoconConfigurationFactory", category = "ConfigurationFactory") @Order(8) public class HoconConfigurationFactory extends JsonConfigurationFactory { private static final String[] FILE_TYPES = {".conf", ".json", ".properties"}; private static final String DEPENDENCY = "com.typesafe.config.ConfigFactory"; private static final int MAX_CONFIG_SIZE = 33_554_432; private final boolean configActive; public HoconConfigurationFactory() { if (!Loader.isClassAvailable(DEPENDENCY)) { LOGGER.debug("Missing dependencies for hocon support"); configActive = false; return; } configActive = true; } @Override protected boolean isActive() { return configActive && super.isActive(); } @Override public Configuration getConfiguration(final ConfigurationSource source) { if (!isActive()) { return null; } return getReformattingJsonConfiguration(source); } static Configuration getReformattingJsonConfiguration(final ConfigurationSource source) { try (InputStream configStream = source.getInputStream()) { byte[] buffer = ByteStreams.toByteArray(ByteStreams.limit(configStream, MAX_CONFIG_SIZE)); String configString = new String(buffer, StandardCharsets.UTF_8); Config config = ConfigFactory.parseString(configString, ConfigParseOptions.defaults() .setOriginDescription(source.getLocation())); // if it kinda looks like a job config, support its overrides and ignore everything else if (config.hasPath("global")) { config = config.getConfig("global") .withFallback(ConfigFactory.load()).resolve() .getConfig("logging"); } else { config = config.resolveWith((ConfigFactory.defaultOverrides()), ConfigResolveOptions.defaults().setAllowUnresolved(true)); } config = config.resolve(); ConfigObject configObject = config.root(); for (String key : configObject.keySet()) { if (key.charAt(0) == '_') { config = config.root().withoutKey(key).toConfig(); } } String json = config.root().render(ConfigRenderOptions.concise()); InputStream fakeStream = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); ConfigurationSource fakeSource; if (source.getFile() != null) { fakeSource = new ConfigurationSource(fakeStream, source.getFile()); } else if (source.getURL() != null) { fakeSource = new ConfigurationSource(fakeStream, source.getURL()); } else { fakeSource = new ConfigurationSource(fakeStream); } return new HoconConfiguration(fakeSource); } catch (final Exception ex) { LOGGER.error("Error parsing {}", source.getLocation(), ex); } return null; } @Override public String[] getSupportedTypes() { return FILE_TYPES; } }