FilterTask.java
/**
* Copyright (c) 2013-2017 Polago AB
* All rights reserved.
*
* 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.
*/
package org.polago.deployconf.task.filter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import org.jdom2.Element;
import org.polago.deployconf.InteractiveConfigurer;
import org.polago.deployconf.group.ConfigGroup;
import org.polago.deployconf.group.ConfigGroupManager;
import org.polago.deployconf.task.AbstractTask;
import org.polago.deployconf.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Deployment Task for filtering tokens in a file.
*/
public class FilterTask extends AbstractTask {
private static Logger logger = LoggerFactory.getLogger(FilterTask.class);
/**
* Task element name in config file.
*/
public static final String DOM_ELEMENT_TASK = "filter";
private static final String DOM_ELEMENT_NAME = "name";
private static final String DOM_ELEMENT_REGEX = "regex";
private static final String DOM_ELEMENT_TOKEN = "token";
private static final String ATTRIBUTE_ENCODING = "encoding";
private Set<FilterToken> tokens;
private String encoding = "UTF-8";
/**
* Public Constructor.
*
* @param groupManager the groupManager to use
*/
public FilterTask(ConfigGroupManager groupManager) {
super(groupManager);
tokens = new LinkedHashSet<FilterToken>();
}
/**
* {@inheritDoc}
*/
@Override
public void deserialize(Element root) throws IOException {
super.deserialize(root);
String enc = root.getAttributeValue(ATTRIBUTE_ENCODING);
if (enc != null) {
encoding = enc;
}
for (Element e : root.getChildren()) {
String name = e.getChildTextTrim(DOM_ELEMENT_NAME);
if (name.length() == 0) {
throw new IllegalStateException("Filter name element does not exists");
}
String regex = e.getChildTextTrim(DOM_ELEMENT_REGEX);
if (regex.length() == 0) {
throw new IllegalStateException("Filter regex element does not exists");
}
String description = e.getChildTextTrim(DOM_ELEMENT_DESCRIPTION);
if (description.length() == 0) {
throw new IllegalStateException("Filter description element does not exists");
}
String defaultValue = e.getChildTextTrim(DOM_ELEMENT_DEFAULT);
String group = e.getAttributeValue(DOM_ATTRIBUTE_GROUP);
String value = null;
if (group != null) {
value = getGroupManager().lookupGroup(group).getProperty(name);
}
if (value == null) {
value = e.getChildTextTrim(DOM_ELEMENT_VALUE);
if (group != null && value != null) {
logger.debug("Populating group {} with value of name {}: {}", group, name, value);
getGroupManager().lookupGroup(group).setProperty(name, value);
}
}
FilterToken t = new FilterToken(name, regex, description, defaultValue, value);
if (group != null) {
t.setGroup(group);
}
t.setCondition(e.getChildTextTrim(DOM_ELEMENT_CONDITION));
logger.debug("Deserializing FilterToken: {}", t);
tokens.add(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void serialize(Element node) throws IOException {
super.serialize(node);
node.setAttribute(ATTRIBUTE_ENCODING, getEncoding());
for (FilterToken t : tokens) {
logger.debug("Serializing FilterToken: {}", t);
Element e = createJDOMElement(DOM_ELEMENT_TOKEN);
e.addContent(createJDOMTextElement(DOM_ELEMENT_NAME, t.getName()));
e.addContent(createJDOMTextElement(DOM_ELEMENT_REGEX, t.getRegex().toString()));
e.addContent(createJDOMCDATAElement(DOM_ELEMENT_DESCRIPTION, t.getDescription()));
e.addContent(createJDOMTextElement(DOM_ELEMENT_DEFAULT, t.getDefaultValue()));
e.addContent(createJDOMTextElement(DOM_ELEMENT_CONDITION, t.getCondition()));
String group = t.getGroup();
if (group != null) {
e.setAttribute(DOM_ATTRIBUTE_GROUP, group);
} else {
e.addContent(createJDOMTextElement(DOM_ELEMENT_VALUE, t.getValue()));
}
node.addContent(e);
}
}
/**
* Gets the tokens property value.
*
* @return the current value of the tokens property
*/
public Set<FilterToken> getTokens() {
return tokens;
}
/**
* Sets the tokens property.
*
* @param tokens the new property value
*/
void setTokens(Set<FilterToken> tokens) {
this.tokens = tokens;
}
/**
* Gets the encoding to use when filtering files.
*
* @return the encoding to use
*/
private String getEncoding() {
return encoding;
}
/**
* {@inheritDoc}
*/
@Override
public void merge(Task other) {
if (other instanceof FilterTask) {
FilterTask oft = (FilterTask) other;
tokens.retainAll(oft.getTokens());
for (FilterToken ot : oft.getTokens()) {
boolean exists = false;
for (FilterToken t : tokens) {
if (t.equals(ot)) {
exists = true;
t.setRegex(ot.getRegex());
t.setDescription(ot.getDescription());
t.setDefaultValue(ot.getDefaultValue());
t.setGroup(ot.getGroup());
t.setCondition(ot.getCondition());
logger.debug("Merging existing FilterToken: {}", t);
break;
}
}
if (!exists) {
logger.debug("Adding new FilterToken: {}", ot);
tokens.add(ot);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isConfigured() throws IOException {
for (FilterToken t : tokens) {
if (evaluateCondition(t.getCondition(), getGroupManager().lookupGroup(t.getGroup()))) {
if (t.getValue() == null || t.getValue().length() == 0) {
logger.debug("FilterToken is not configured: {}", t);
return false;
}
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean configureInteractively(InteractiveConfigurer configurer, boolean force) throws Exception {
boolean configured = true;
for (FilterToken t : tokens) {
ConfigGroup group = getGroupManager().lookupGroup(t.getGroup());
if (evaluateCondition(t.getCondition(), group)
&& (force || t.getValue() == null || t.getValue().length() == 0)) {
configured = configureTokenInteractively(t, configurer);
if (configured) {
group.setProperty(t.getName(), t.getValue());
} else {
return configured;
}
}
}
return configured;
}
/**
* {@inheritDoc}
*/
@Override
public String getSerializedName() {
return DOM_ELEMENT_TASK;
}
/**
* {@inheritDoc}
*/
@Override
public void apply(InputStream source, OutputStream destination) throws Exception {
InputStreamReader in = new InputStreamReader(source, getEncoding());
BufferedReader reader = new BufferedReader(in);
OutputStreamWriter out = new OutputStreamWriter(destination, getEncoding());
BufferedWriter writer = new BufferedWriter(out);
String line = reader.readLine();
while (line != null) {
line = filterLine(line);
writer.write(line);
line = reader.readLine();
writer.newLine();
}
writer.flush();
}
/**
* Filter the given line.
*
* @param line the line to process
* @return the filtered line
* @throws IOException indicating IO Error
*/
private String filterLine(String line) throws IOException {
for (FilterToken t : getTokens()) {
ConfigGroup group = getGroupManager().lookupGroup(t.getGroup());
if (evaluateCondition(t.getCondition(), group)) {
Matcher matcher = t.getRegex().matcher(line);
String value = expandPropertyExpression(t.getValue(), group);
line = matcher.replaceAll(value);
}
}
return line;
}
/**
* Configure a FilterToken by asking the user.
*
* @param t the FilterToken to configure
* @param configurer the InteractiveConfigurer to use
* @return true if the token was configured
* @throws IOException indicating IO failure
*/
private boolean configureTokenInteractively(FilterToken t, InteractiveConfigurer configurer) throws IOException {
boolean result = false;
logger.debug("Configure interactively: {}", t.getRegex().toString());
String defaultValue = t.getValue();
if (defaultValue == null || defaultValue.length() == 0) {
defaultValue = t.getDefaultValue();
}
String value = configurer.configure(t.getName(), t.getDescription(), defaultValue);
logger.debug("Configure interactively result for '{}({})': {}", t.getName(), t.getRegex().toString(), value);
if (value != null) {
t.setValue(value);
result = true;
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "FilterTask [path=" + getPath() + ", tokens=" + tokens + ", encoding=" + encoding + "]";
}
}