/**
* Copyright 2011 Intuit Inc. All Rights Reserved
*/
package com.intuit.tank.proxy.config;
/*
* #%L
* proxy-extension
* %%
* Copyright (C) 2011 - 2015 Intuit Inc.
* %%
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* #L%
*/
import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.SubnodeConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.commons.configuration.tree.ConfigurationNode;
import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* ProxyConfiguration
*
* @author dangleton
*
*/
public class CommonsProxyConfiguration implements ProxyConfiguration {
private static final Logger LOG = LogManager.getLogger(CommonsProxyConfiguration.class);
public static final String DEFAULT_CONFIG = "recording-proxy-config-default.xml";
public static final String SUGGESTED_CONFIG_NAME = "recording-proxy-config.xml";
private String configPath = "recording-proxy-config.xml";
private XMLConfiguration config;
private FileChangedReloadingStrategy reloadingStrategy;
private Set<ConfigInclusionExclusionRule> exclusions = new HashSet<ConfigInclusionExclusionRule>();
private Set<ConfigInclusionExclusionRule> inclusions = new HashSet<ConfigInclusionExclusionRule>();
private Set<ConfigInclusionExclusionRule> bodyInclusions = new HashSet<ConfigInclusionExclusionRule>();
private Set<ConfigInclusionExclusionRule> bodyExclusions = new HashSet<ConfigInclusionExclusionRule>();
/**
* test constructor
*
*/
public CommonsProxyConfiguration(String configPath) {
this.configPath = configPath != null ? configPath : DEFAULT_CONFIG;
readConfig();
}
/**
*
*/
public CommonsProxyConfiguration() {
this(null);
}
/**
* @{inheritDoc
*/
public int getPort() {
return config.getInt("proxy-port", 8888);
}
/**
* @{inheritDoc
*/
@Override
public boolean isFollowRedirects() {
return config.getBoolean("follow-redirects", true);
}
/**
* @{inheritDoc
*/
public String getOutputFile() {
return config.getString("output-file", new File("recordedOutput.xml").getAbsolutePath());
}
/**
* @{inheritDoc
*/
@Override
public String getCertificateAuthorityPath() {
return config.getString("certificate-authority-path", new File("auto_generated_ca.p12").getAbsolutePath());
}
/**
* @{inheritDoc
*/
public Set<ConfigInclusionExclusionRule> getExclusions() {
if (needsReload()) {
readConfig();
}
return exclusions;
}
/**
* @{inheritDoc
*/
public Set<ConfigInclusionExclusionRule> getInclusions() {
if (needsReload()) {
readConfig();
}
return inclusions;
}
/**
* @{inheritDoc
*/
public Set<ConfigInclusionExclusionRule> getBodyInclusions() {
if (needsReload()) {
readConfig();
}
return bodyInclusions;
}
/**
* @{inheritDoc
*/
public Set<ConfigInclusionExclusionRule> getBodyExclusions() {
if (needsReload()) {
readConfig();
}
return bodyExclusions;
}
/**
* Constructor pulls file out of the jar or reads from disk and sets up refresh policy.
*
* @param expressionEngine
* the expression engine to use. Null results in default expression engine
*/
private void readConfig() {
try {
XPathExpressionEngine expressionEngine = new XPathExpressionEngine();
if (reloadingStrategy == null) {
reloadingStrategy = new FileChangedReloadingStrategy();
reloadingStrategy.setRefreshDelay(0);
}
File configFile = new File(configPath);
System.out.println(configFile.getAbsolutePath());
if (configFile.exists() && configFile.isFile()) {
try {
config = new XMLConfiguration(configFile);
} catch (Exception e) {
LOG.error("Error parsing configFile " + configFile.getAbsolutePath() + ": " + e, e);
}
}
if (config == null) {
// Load a default from the classpath:
LOG.info("Reading default configuration " + DEFAULT_CONFIG + " from classpath...");
// Note: we don't let new XMLConfiguration() lookup the resource
// url directly because it may not be able to find the desired
// classloader to load the URL from.
URL configResourceUrl = this.getClass().getClassLoader().getResource(DEFAULT_CONFIG);
if (configResourceUrl == null) {
throw new RuntimeException("unable to load resource: " + configPath);
}
config = new XMLConfiguration(configResourceUrl);
// // Copy over a default configuration since none exists:
// // Ensure data dir location exists:
// configFile = new File(DEFAULT_CONFIG);
// // if (!configFile.getParentFile().exists() && !configFile.getParentFile().mkdirs()) {
// // throw new RuntimeException("could not create directories.");
// // }
// LOG.info("Saving default configuration to file " + configFile.getAbsolutePath());
// config.save(configFile);
}
if (expressionEngine != null) {
config.setExpressionEngine(expressionEngine);
}
// reload at most once per thirty seconds on configuration queries.
config.setReloadingStrategy(reloadingStrategy);
initConfig();
} catch (ConfigurationException e) {
throw new RuntimeException(e);
}
}
public void saveConfig(File configFile) throws ConfigurationException {
config.save(configFile);
}
/**
*
*/
private void initConfig() {
exclusions = parseInclusionExclusions("exclusions");
inclusions = parseInclusionExclusions("inclusions");
bodyExclusions = parseInclusionExclusions("body-exclusions");
bodyInclusions = parseInclusionExclusions("body-inclusions");
}
/**
* @param string
* @return
*/
private Set<ConfigInclusionExclusionRule> parseInclusionExclusions(String key) {
Set<ConfigInclusionExclusionRule> ret = new HashSet<ConfigInclusionExclusionRule>();
SubnodeConfiguration groupConfig = config.configurationAt(key);
if (groupConfig != null) {
@SuppressWarnings("unchecked") List<HierarchicalConfiguration> list = groupConfig.configurationsAt("rule");
for (HierarchicalConfiguration c : list) {
ret.add(new ConfigInclusionExclusionRule(getTransactionPart(c), c.getString("@header", "all"),
getMatchType(c), c.getString("")));
}
}
return ret;
}
/**
* @param c
* @return
*/
private MatchType getMatchType(HierarchicalConfiguration c) {
MatchType ret = MatchType.contains;
String string = c.getString("@match");
if (string != null) {
try {
ret = MatchType.valueOf(string);
} catch (Exception e) {
LOG.warn("Illegal MatchType value: " + string);
}
}
return ret;
}
/**
* @param c
* @return
*/
private TransactionPart getTransactionPart(HierarchicalConfiguration c) {
TransactionPart ret = TransactionPart.both;
String string = c.getString("@check");
if (string != null) {
try {
ret = TransactionPart.valueOf(string);
} catch (Exception e) {
LOG.warn("Illegal TransactionPart value: " + string);
}
}
return ret;
}
/**
* checks if the config needs to be reloaded.
*/
public boolean needsReload() {
return (config == null || config.getReloadingStrategy().reloadingRequired());
}
public static boolean save(int port, boolean followRedirect, String outputFile,
Set<ConfigInclusionExclusionRule> inclusions,
Set<ConfigInclusionExclusionRule> exclusions,
Set<ConfigInclusionExclusionRule> bodyInclusions,
Set<ConfigInclusionExclusionRule> bodyExclusions,
String fileName) {
ConfigurationNode node = getConfNode("recording-proxy-config", "", false);
ConfigurationNode portNode = getConfNode("proxy-port", String.valueOf(port), false);
ConfigurationNode followRedirectNode = getConfNode("follow-redirects", Boolean.toString(followRedirect), false);
ConfigurationNode outputFileNode = getConfNode("output-file", outputFile, false);
ConfigurationNode inclusionsNode = getConfNode("inclusions", "", false);
ConfigurationNode exclusionsNode = getConfNode("exclusions", "", false);
ConfigurationNode bodyInclusionsNode = getConfNode("body-inclusions", "", false);
ConfigurationNode bodyExclusionsNode = getConfNode("body-exclusions", "", false);
updateRuleParentNode(inclusions, inclusionsNode);
updateRuleParentNode(exclusions, exclusionsNode);
updateRuleParentNode(bodyInclusions, bodyInclusionsNode);
updateRuleParentNode(bodyExclusions, bodyExclusionsNode);
node.addChild(portNode);
node.addChild(followRedirectNode);
node.addChild(outputFileNode);
node.addChild(inclusionsNode);
node.addChild(exclusionsNode);
node.addChild(bodyInclusionsNode);
node.addChild(bodyExclusionsNode);
HierarchicalConfiguration hc = new HierarchicalConfiguration();
hc.setRootNode(node);
XMLConfiguration xmlConfiguration = new XMLConfiguration(hc);
xmlConfiguration.setRootNode(node);
try {
xmlConfiguration.save(new File(fileName));
} catch (ConfigurationException e) {
e.printStackTrace();
}
return true;
}
public static void updateRuleParentNode(Set<ConfigInclusionExclusionRule> rule, ConfigurationNode parentNode) {
for (ConfigInclusionExclusionRule configInclusionExclusionRule : rule) {
ConfigurationNode ruleNode = getConfNode("rule", configInclusionExclusionRule.getValue(), false);
ConfigurationNode checkNode = getConfNode("check", configInclusionExclusionRule.getTransactionPart()
.toString(), true);
ConfigurationNode matchNode = getConfNode("match", configInclusionExclusionRule.getMatch().toString(), true);
ConfigurationNode headerNode = getConfNode("header", configInclusionExclusionRule.getHeader(), true);
ruleNode.addAttribute(checkNode);
ruleNode.addAttribute(matchNode);
ruleNode.addAttribute(headerNode);
parentNode.addChild(ruleNode);
}
}
public static ConfigurationNode getConfNode(String name, String value, boolean attributeFlag) {
ConfigurationNode confNode = new HierarchicalConfiguration.Node(name, value);
confNode.setAttribute(attributeFlag);
return confNode;
}
}