View Javadoc
1   /**
2    * Copyright (c) 2013-2017 Polago AB
3    * All rights reserved.
4    *
5    * Permission is hereby granted, free of charge, to any person obtaining
6    * a copy of this software and associated documentation files (the
7    * "Software"), to deal in the Software without restriction, including
8    * without limitation the rights to use, copy, modify, merge, publish,
9    * distribute, sublicense, and/or sell copies of the Software, and to
10   * permit persons to whom the Software is furnished to do so, subject to
11   * the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be
14   * included in all copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   */
24  
25  package org.polago.deployconf.task.filter;
26  
27  import java.io.BufferedReader;
28  import java.io.BufferedWriter;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.InputStreamReader;
32  import java.io.OutputStream;
33  import java.io.OutputStreamWriter;
34  import java.util.LinkedHashSet;
35  import java.util.Set;
36  import java.util.regex.Matcher;
37  
38  import org.jdom2.Element;
39  import org.polago.deployconf.InteractiveConfigurer;
40  import org.polago.deployconf.group.ConfigGroup;
41  import org.polago.deployconf.group.ConfigGroupManager;
42  import org.polago.deployconf.task.AbstractTask;
43  import org.polago.deployconf.task.Task;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  /**
48   * Deployment Task for filtering tokens in a file.
49   */
50  public class FilterTask extends AbstractTask {
51  
52      private static Logger logger = LoggerFactory.getLogger(FilterTask.class);
53  
54      /**
55       * Task element name in config file.
56       */
57      public static final String DOM_ELEMENT_TASK = "filter";
58  
59      private static final String DOM_ELEMENT_NAME = "name";
60  
61      private static final String DOM_ELEMENT_REGEX = "regex";
62  
63      private static final String DOM_ELEMENT_TOKEN = "token";
64  
65      private static final String ATTRIBUTE_ENCODING = "encoding";
66  
67      private Set<FilterToken> tokens;
68  
69      private String encoding = "UTF-8";
70  
71      /**
72       * Public Constructor.
73       *
74       * @param groupManager the groupManager to use
75       */
76      public FilterTask(ConfigGroupManager groupManager) {
77          super(groupManager);
78          tokens = new LinkedHashSet<FilterToken>();
79      }
80  
81      /**
82       * {@inheritDoc}
83       */
84      @Override
85      public void deserialize(Element root) throws IOException {
86          super.deserialize(root);
87          String enc = root.getAttributeValue(ATTRIBUTE_ENCODING);
88          if (enc != null) {
89              encoding = enc;
90          }
91          for (Element e : root.getChildren()) {
92              String name = e.getChildTextTrim(DOM_ELEMENT_NAME);
93              if (name.length() == 0) {
94                  throw new IllegalStateException("Filter name element does not exists");
95              }
96              String regex = e.getChildTextTrim(DOM_ELEMENT_REGEX);
97              if (regex.length() == 0) {
98                  throw new IllegalStateException("Filter regex element does not exists");
99              }
100             String description = e.getChildTextTrim(DOM_ELEMENT_DESCRIPTION);
101             if (description.length() == 0) {
102                 throw new IllegalStateException("Filter description element does not exists");
103             }
104             String defaultValue = e.getChildTextTrim(DOM_ELEMENT_DEFAULT);
105 
106             String group = e.getAttributeValue(DOM_ATTRIBUTE_GROUP);
107             String value = null;
108 
109             if (group != null) {
110                 value = getGroupManager().lookupGroup(group).getProperty(name);
111             }
112 
113             if (value == null) {
114                 value = e.getChildTextTrim(DOM_ELEMENT_VALUE);
115                 if (group != null && value != null) {
116                     logger.debug("Populating group {} with value of name {}: {}", group, name, value);
117                     getGroupManager().lookupGroup(group).setProperty(name, value);
118                 }
119             }
120 
121             FilterToken t = new FilterToken(name, regex, description, defaultValue, value);
122 
123             if (group != null) {
124                 t.setGroup(group);
125             }
126 
127             t.setCondition(e.getChildTextTrim(DOM_ELEMENT_CONDITION));
128 
129             logger.debug("Deserializing FilterToken: {}", t);
130 
131             tokens.add(t);
132         }
133     }
134 
135     /**
136      * {@inheritDoc}
137      */
138     @Override
139     public void serialize(Element node) throws IOException {
140         super.serialize(node);
141         node.setAttribute(ATTRIBUTE_ENCODING, getEncoding());
142         for (FilterToken t : tokens) {
143             logger.debug("Serializing FilterToken: {}", t);
144             Element e = createJDOMElement(DOM_ELEMENT_TOKEN);
145             e.addContent(createJDOMTextElement(DOM_ELEMENT_NAME, t.getName()));
146             e.addContent(createJDOMTextElement(DOM_ELEMENT_REGEX, t.getRegex().toString()));
147             e.addContent(createJDOMCDATAElement(DOM_ELEMENT_DESCRIPTION, t.getDescription()));
148             e.addContent(createJDOMTextElement(DOM_ELEMENT_DEFAULT, t.getDefaultValue()));
149             e.addContent(createJDOMTextElement(DOM_ELEMENT_CONDITION, t.getCondition()));
150 
151             String group = t.getGroup();
152             if (group != null) {
153                 e.setAttribute(DOM_ATTRIBUTE_GROUP, group);
154             } else {
155                 e.addContent(createJDOMTextElement(DOM_ELEMENT_VALUE, t.getValue()));
156             }
157 
158             node.addContent(e);
159         }
160     }
161 
162     /**
163      * Gets the tokens property value.
164      *
165      * @return the current value of the tokens property
166      */
167     public Set<FilterToken> getTokens() {
168         return tokens;
169     }
170 
171     /**
172      * Sets the tokens property.
173      *
174      * @param tokens the new property value
175      */
176     void setTokens(Set<FilterToken> tokens) {
177         this.tokens = tokens;
178     }
179 
180     /**
181      * Gets the encoding to use when filtering files.
182      *
183      * @return the encoding to use
184      */
185     private String getEncoding() {
186         return encoding;
187     }
188 
189     /**
190      * {@inheritDoc}
191      */
192     @Override
193     public void merge(Task other) {
194         if (other instanceof FilterTask) {
195             FilterTask oft = (FilterTask) other;
196             tokens.retainAll(oft.getTokens());
197 
198             for (FilterToken ot : oft.getTokens()) {
199                 boolean exists = false;
200                 for (FilterToken t : tokens) {
201                     if (t.equals(ot)) {
202                         exists = true;
203                         t.setRegex(ot.getRegex());
204                         t.setDescription(ot.getDescription());
205                         t.setDefaultValue(ot.getDefaultValue());
206                         t.setGroup(ot.getGroup());
207                         t.setCondition(ot.getCondition());
208                         logger.debug("Merging existing FilterToken: {}", t);
209                         break;
210                     }
211                 }
212                 if (!exists) {
213                     logger.debug("Adding new FilterToken: {}", ot);
214                     tokens.add(ot);
215                 }
216             }
217         }
218     }
219 
220     /**
221      * {@inheritDoc}
222      */
223     @Override
224     public boolean isConfigured() throws IOException {
225         for (FilterToken t : tokens) {
226             if (evaluateCondition(t.getCondition(), getGroupManager().lookupGroup(t.getGroup()))) {
227                 if (t.getValue() == null || t.getValue().length() == 0) {
228                     logger.debug("FilterToken is not configured: {}", t);
229                     return false;
230                 }
231             }
232         }
233 
234         return true;
235     }
236 
237     /**
238      * {@inheritDoc}
239      */
240     @Override
241     public boolean configureInteractively(InteractiveConfigurer configurer, boolean force) throws Exception {
242 
243         boolean configured = true;
244 
245         for (FilterToken t : tokens) {
246             ConfigGroup group = getGroupManager().lookupGroup(t.getGroup());
247             if (evaluateCondition(t.getCondition(), group)
248                 && (force || t.getValue() == null || t.getValue().length() == 0)) {
249                 configured = configureTokenInteractively(t, configurer);
250                 if (configured) {
251                     group.setProperty(t.getName(), t.getValue());
252                 } else {
253                     return configured;
254                 }
255             }
256         }
257 
258         return configured;
259     }
260 
261     /**
262      * {@inheritDoc}
263      */
264     @Override
265     public String getSerializedName() {
266         return DOM_ELEMENT_TASK;
267     }
268 
269     /**
270      * {@inheritDoc}
271      */
272     @Override
273     public void apply(InputStream source, OutputStream destination) throws Exception {
274 
275         InputStreamReader in = new InputStreamReader(source, getEncoding());
276         BufferedReader reader = new BufferedReader(in);
277 
278         OutputStreamWriter out = new OutputStreamWriter(destination, getEncoding());
279         BufferedWriter writer = new BufferedWriter(out);
280 
281         String line = reader.readLine();
282         while (line != null) {
283             line = filterLine(line);
284             writer.write(line);
285             line = reader.readLine();
286             writer.newLine();
287         }
288         writer.flush();
289     }
290 
291     /**
292      * Filter the given line.
293      *
294      * @param line the line to process
295      * @return the filtered line
296      * @throws IOException indicating IO Error
297      */
298     private String filterLine(String line) throws IOException {
299         for (FilterToken t : getTokens()) {
300             ConfigGroup group = getGroupManager().lookupGroup(t.getGroup());
301             if (evaluateCondition(t.getCondition(), group)) {
302                 Matcher matcher = t.getRegex().matcher(line);
303                 String value = expandPropertyExpression(t.getValue(), group);
304                 line = matcher.replaceAll(value);
305             }
306         }
307 
308         return line;
309 
310     }
311 
312     /**
313      * Configure a FilterToken by asking the user.
314      *
315      * @param t the FilterToken to configure
316      * @param configurer the InteractiveConfigurer to use
317      * @return true if the token was configured
318      * @throws IOException indicating IO failure
319      */
320     private boolean configureTokenInteractively(FilterToken t, InteractiveConfigurer configurer) throws IOException {
321 
322         boolean result = false;
323 
324         logger.debug("Configure interactively: {}", t.getRegex().toString());
325 
326         String defaultValue = t.getValue();
327         if (defaultValue == null || defaultValue.length() == 0) {
328             defaultValue = t.getDefaultValue();
329         }
330 
331         String value = configurer.configure(t.getName(), t.getDescription(), defaultValue);
332         logger.debug("Configure interactively result for '{}({})': {}", t.getName(), t.getRegex().toString(), value);
333         if (value != null) {
334             t.setValue(value);
335             result = true;
336         }
337 
338         return result;
339     }
340 
341     /**
342      * {@inheritDoc}
343      */
344     @Override
345     public String toString() {
346         return "FilterTask [path=" + getPath() + ", tokens=" + tokens + ", encoding=" + encoding + "]";
347     }
348 
349 }