Juan Hernandez has uploaded a new change for review. Change subject: core: Simple template engine ......................................................................
core: Simple template engine This patch introduces a simple template engine that uses a similar syntax to JSP. This template engine will be used by later patches to simplify the generation of LDAP queries. Change-Id: I14bf2a4d707002c5a1ddcd4ae1e7729957f359b8 Signed-off-by: Juan Hernandez <[email protected]> --- A backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/Template.java A backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateCompiler.java A backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateException.java A backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateOption.java A backend/manager/modules/utils/src/test/java/org/ovirt/engine/core/utils/templates/TemplateTest.java A backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.expected A backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.template A backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.expected A backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.template A backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.expected A backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.template 11 files changed, 918 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/50/14650/1 diff --git a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/Template.java b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/Template.java new file mode 100644 index 0000000..cf94ab1 --- /dev/null +++ b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/Template.java @@ -0,0 +1,121 @@ +package org.ovirt.engine.core.utils.templates; + +import java.io.CharArrayWriter; +import java.io.PrintWriter; +import java.util.Map; + +import javax.script.Bindings; +import javax.script.CompiledScript; +import javax.script.ScriptException; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +/** + * This class represents a precompiled template. Don't try to instantiate it + * directly, use {@link TemplateCompiler#compile(String, TemplateOption...))} + * instead to compile new templates. + */ +public class Template { + // The log: + private static final Logger log = Logger.getLogger(Template.class); + + // The source of the template, before processing it: + private String source; + + // The script generated from the source of the template and compiled for + // better performance: + private CompiledScript script; + + /** + * This constructor is intended to be use by the template compiler once + * the source is already compiled, don't try to use it directly. + * + * @param source the source of the template + * @param script the script used to evaluate the template + */ + Template(String source, CompiledScript script) { + this.source = source; + this.script = script; + } + + /** + * Returns the source code of the template. + */ + public String getSource() { + return source; + } + + /** + * Evaluates the template with the given parameters. Each of the entries + * of the parameters map will be converted into an object available + * in expressions inside the map. + * + * @param parameters a map containing the parameters to pass to the + * template + * @return returns the result of evaluating the template + * @throws TemplateException if anything fails during the evaluation of + * the template + */ + public String eval(Map<String, Object> parameters) throws TemplateException { + // Prepare the stream where the script will generate the + // output of the template: + CharArrayWriter chars = new CharArrayWriter(source.length()); + PrintWriter out = new PrintWriter(chars); + + // Prepare a set of bindings containing the parameters given by the + // user plus the builtin objects: + Bindings bindings = script.getEngine().createBindings(); + if (parameters != null) { + bindings.putAll(parameters); + } + bindings.put("out", out); + + // Run the script: + try { + script.eval(bindings); + } + catch (ScriptException exception) { + throw new TemplateException("Error evaluating template.", exception); + } + + // Flush the output in case the generate script didn't: + out.flush(); + + String text = chars.toString(); + + // Dump the generated text to the log: + if (log.isInfoEnabled()) { + log.info("Template output follows:\n" + text); + } + + return text; + } + + /** + * Return the string representation of the template, which is actually + * the source used to create it. + * + * @return the string representation of the template + */ + @Override + public String toString() { + return source; + } + + @Override + public int hashCode() { + return source.hashCode(); + } + + @Override + public boolean equals(Object obj) { + try { + Template that = (Template) obj; + return StringUtils.equals(this.source, that.source); + } + catch (ClassCastException exception) { + return false; + } + } +} diff --git a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateCompiler.java b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateCompiler.java new file mode 100644 index 0000000..7da243d --- /dev/null +++ b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateCompiler.java @@ -0,0 +1,584 @@ +package org.ovirt.engine.core.utils.templates; + +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +/** + * As simple templating engine. Templates hava a syntax similar to JSP, but + * using Javascript for the scriptlets instead of Java. An example of a + * template that generates a LDAP filter could be the following: + * + * <pre> + * (& + * (dc=${domain}) + * (| + * <% for (id in ids) %> + * (uid=${id}) + * <% } %> + * ) + * ) + * </pre> + * + * This template is assuming that it will be evaluated passing as parameters + * a <code>domain</code> and an array of <code>ids</code>. The template can + * be compiled and evaluated as follows: + * + * <pre> + * {@code + * try { + * String source = "(&(dc=..."; + * TemplateCompiler compiler = TemplateCompiler.getInstance(); + * Template template = compiler.compile(source, TemplateOptions.IGNORE_WHITESPACE); + * Map<String, Object> parameters = new HashMap<>(); + * parameters.put("domain", "example.com"); + * int[] ids = new int[] { 1, 2, 3 }; + * parameters.put("ids", ids); + * String result = template.eval(parameters); + * ... + * } + * catch (TemplateException exception) { + * ... + * } + * </pre> + * + * Once a template is compiled it can be safely reused, even from different + * threads. + * + * Compiled templates are cached, so if the same source is provided twice the + * second invocation to {@link #compile(String, TemplateOption...)} will + * return the same template. This cache is currently never cleaned, so refrain + * from using templates that are generated dynamically as they may cause a + * memory leak. + */ +public class TemplateCompiler { + // The log: + private static final Logger log = Logger.getLogger(TemplateCompiler.class); + + // The name of the scripting language to use: + private static final String LANGUAGE = "JavaScript"; + + // This is a singleton, and this is the instance: + private static volatile TemplateCompiler instance; + + /** + * Get the reference to the singleton instance of the template + * compiler. + */ + public static TemplateCompiler getInstance() { + if (instance == null) { + synchronized(TemplateCompiler.class) { + instance = new TemplateCompiler(); + } + } + return instance; + } + + // A cache containing already compiled templates, indexe by the source + // code of the template: + private Map<Key, Template> cache = new ConcurrentHashMap<>(); + + // The scripting engine used to compile the scripts generated from + // the templates: + private Compilable compiler; + + private TemplateCompiler() { + // Find the scripting engine: + final ScriptEngineManager manager = new ScriptEngineManager(); + final ScriptEngine engine = manager.getEngineByName(LANGUAGE); + if (engine == null) { + log.error( + "Can't find scripting engine for language \"" + LANGUAGE + "\"." + ); + return; + } + + // For performance reasons we only support scripting languages that + // support compilation, so we check it: + try { + compiler = (Compilable) engine; + } + catch (ClassCastException exception) { + log.error( + "The scripting language \"" + LANGUAGE + "\" doesn't support " + + "compilation.", exception + ); + } + } + + /** + * The states of the machine used to parse the templates. + */ + private static enum State { + BEGIN, + MARK_0, + MARK_1, + COMMENT_0, + COMMENT_1, + COMMENT_2, + COMMENT_3, + COMMENT_4, + SCRIPTLET_0, + SCRIPTLET_1, + VALUE_0, + VALUE_1, + EXPRESSION_0, + EXPRESSION_1, + END + } + + /** + * The key used to index templates in the cache. It is composed by the + * source of the template and the compilation options. + */ + private static final class Key { + private String source; + private Set<TemplateOption> options; + + public Key(String source, Set<TemplateOption> options) { + this.source = source; + this.options = options; + } + + @Override + public int hashCode () { + return + source.hashCode() + + options.hashCode() + ; + } + + @Override + public boolean equals(Object obj) { + try { + Key that = (Key) obj; + return + StringUtils.equals(this.source, that.source) && + this.options.equals(that.options) + ; + } + catch (ClassCastException exception) { + return false; + } + } + } + + private void handleText(final StringBuilder buffer, final PrintWriter out, final Set<TemplateOption> options) { + // Check the length of the text and do nothing if it is empty: + final int length = buffer.length(); + if (length == 0) { + return; + } + + // Generate the statement that generates the given text, escaping the + // content so it will be a correct string of the scripting language: + out.print("out.print(\""); + for (int i = 0; i < length; i++) { + final char c = buffer.charAt(i); + if (options.contains(TemplateOption.IGNORE_WHITESPACE) && Character.isWhitespace(c)) { + continue; + } + if (c == '\n') { + out.print("\\n"); + } + else if (c == '\t') { + out.print("\\t"); + } + else if (c == '"') { + out.print("\\"); + out.print(c); + } + else { + out.print(c); + } + } + out.println("\");"); + + // Clear the buffer: + buffer.setLength(0); + } + + private void handleComment(final StringBuilder buffer, final PrintWriter out) { + buffer.setLength(0); + } + + private void handleScriptlet(final StringBuilder buffer, final PrintWriter out) { + out.print(buffer); + buffer.setLength(0); + } + + private void handleValue(final StringBuilder buffer, final PrintWriter out) { + out.print("out.print(" + buffer + ");"); + buffer.setLength(0); + } + + private void handleExpression(final StringBuilder buffer, final PrintWriter out) { + out.print("out.print(" + buffer + ");"); + buffer.setLength(0); + } + + private void handleError(final String message, final int line, final int column) throws TemplateException { + throw new TemplateException(message + "(" + line + ", " + column + ")."); + } + + public void generateScript(final Reader in, final Writer out, final Set<TemplateOption> options) throws TemplateException { + // Prepare the output for the generated script source: + final PrintWriter printer = new PrintWriter(out); + + // Initialize the line and column indexes: + int line = 0; + int column = 0; + + // Initialize the state machine: + State state = State.BEGIN; + final StringBuilder buffer = new StringBuilder(); + + while (state != State.END) { + + // Read the next character from the input: + int c; + try { + c = in.read(); + } + catch (IOException exception) { + throw new TemplateException("IO error while generating script.", exception); + } + + // Update the line and column indexes: + if (c == '\n') { + line++; + column = 0; + } + else { + column++; + } + + // Process the next character according to the current state: + switch (state) { + + case BEGIN: + if (c == -1) { + handleText(buffer, printer, options); + state = State.END; + } + else if (c == '<') { + handleText(buffer, printer, options); + state = State.MARK_0; + } + else if (c == '$') { + handleText(buffer, printer, options); + state = State.EXPRESSION_0; + } + else { + buffer.append((char) c); + state = State.BEGIN; + } + break; + + case MARK_0: // < + if (c == -1) { + buffer.append('<'); + handleText(buffer, printer, options); + state = State.END; + } + else if (c == '<') { + buffer.append('<'); + state = State.MARK_0; + } + else if (c == '%') { + handleText(buffer, printer, options); + state = State.MARK_1; + } + else { + buffer.append('<'); + buffer.append((char) c); + state = State.BEGIN; + } + break; + + case MARK_1: // <% + if (c == -1) { + handleError("EOF inside mark", line, column); + } + else if (c == '-') { + state = State.COMMENT_0; + } + else if (c == '=') { + handleText(buffer, printer, options); + state = State.VALUE_0; + } + else if (c == '%') { + handleText(buffer, printer, options); + state = State.SCRIPTLET_1; + } + else { + buffer.append((char) c); + state = State.SCRIPTLET_0; + } + break; + + case COMMENT_0: // <%- + if (c == -1) { + handleError("EOF inside comment", line, column); + } + else if (c == '-') { + state = State.COMMENT_1; + } + else { + buffer.append('-'); + buffer.append((char) c); + state = State.SCRIPTLET_0; + } + break; + + case COMMENT_1: // <%-- + if (c == -1) { + handleError("EOF inside comment", line, column); + } + else if (c == '-') { + state = State.COMMENT_2; + } + else { + buffer.append((char) c); + state = State.COMMENT_1; + } + break; + + case COMMENT_2: // <%--.- + if (c == -1) { + handleError("EOF inside comment", line, column); + } + else if (c == '-') { + state = State.COMMENT_3; + } + else { + buffer.append('-'); + buffer.append((char) c); + state = State.COMMENT_1; + } + break; + + case COMMENT_3: // <%--.-- + if (c == -1) { + handleError("EOF inside comment", line, column); + } + else if (c == '%') { + state = State.COMMENT_4; + } + else if (c == '-') { + buffer.append('-'); + state = State.COMMENT_3; + } + else { + buffer.append('-'); + buffer.append((char) c); + state = State.COMMENT_1; + } + break; + + case COMMENT_4: // <%--.--% + if (c == -1) { + handleError("EOF inside comment", line, column); + } + else if (c == '>') { + handleComment(buffer, printer); + state = State.BEGIN; + } + else if (c == '-') { + buffer.append("--%"); + state = State.COMMENT_2; + } + else { + buffer.append("--%"); + buffer.append((char) c); + state = State.COMMENT_1; + } + break; + + case SCRIPTLET_0: // <%[^%]* + if (c == -1) { + handleError("EOF inside mark", line, column); + } + else if (c == '%') { + state = State.SCRIPTLET_1; + } + else { + buffer.append((char) c); + state = State.SCRIPTLET_0; + } + break; + + case SCRIPTLET_1: // <%[^%]*% + if (c == -1) { + handleError("EOF inside scriptlet", line, column); + } + else if (c == '>') { + handleScriptlet(buffer, printer); + state = State.BEGIN; + } + else { + buffer.append('%'); + buffer.append((char) c); + state = State.SCRIPTLET_0; + } + break; + + case VALUE_0: // <%=[^%]* + if (c == -1) { + handleError("EOF inside value", line, column); + } + else if (c == '%') { + state = State.VALUE_1; + } + else { + buffer.append((char) c); + state = State.VALUE_0; + } + break; + + case VALUE_1: // <%=[^%]*% + if (c == -1) { + handleError("EOF inside expression", line, column); + } + else if (c == '>') { + handleValue(buffer, printer); + state = State.BEGIN; + } + else { + buffer.append('%'); + buffer.append((char) c); + state = State.VALUE_0; + } + break; + + case EXPRESSION_0: // $ + if (c == -1) { + handleError("EOF inside expression", line, column); + } + else if (c == '{') { + state = State.EXPRESSION_1; + } + else { + buffer.append('$'); + buffer.append((char) c); + state = State.BEGIN; + } + break; + + case EXPRESSION_1: // ${ + if (c == -1) { + handleError("EOF inside expression", line, column); + } + else if (c == '}') { + handleExpression(buffer, printer); + state = State.BEGIN; + } + else { + buffer.append((char) c); + state = State.EXPRESSION_1; + } + break; + + case END: + // Will never happen, but helps avoid warnings from the + // compiler and other code checking tools. + break; + } + } + } + + /** + * Generates the script corresponding to the template text and compiles + * it so that execution will have better performance. The returned + * template object can then be used repeatedly and even simultaneously + * from different threads. + * + * @param source the source of the template + * @param options flags that alter the behaviour of the template compiler + * @return returns a template object that can then be repeatedly used + * to evaluate the template with different parameters + * @throws TemplateException if something fails during the compilation + * of the template + */ + public Template compile(final String source, final Set<TemplateOption> options) throws TemplateException { + // Check if the template is available in the cache (note that this check + // is not atomic, so if two threads invoke this method simultaeously + // the template can end up being compiled more than once, but this is + // not a big issue, as templates are inmutable): + Key key = new Key(source, options); + Template template = cache.get(key); + if (template != null) { + return template; + } + + // Check that we have a script compiler available: + if (compiler == null) { + throw new TemplateException("Script compiler is not available."); + } + + // Dump the template source to the log: + if (log.isInfoEnabled()) { + log.info("Template source follows:\n" + source); + } + + // Generate the source of the script from the template text: + final CharArrayReader sourceReader = new CharArrayReader(source.toCharArray()); + final CharArrayWriter scriptWriter = new CharArrayWriter(); + generateScript(sourceReader, scriptWriter, options); + final char[] scriptSource = scriptWriter.toCharArray(); + + // Dump the generated script source to the log: + if (log.isInfoEnabled()) { + String script = new String(scriptSource); + log.info("Template script follows:\n" + script); + } + + // Compile the generated script: + final CharArrayReader scriptReader = new CharArrayReader(scriptSource); + CompiledScript script; + try { + script = compiler.compile(scriptReader); + } + catch (ScriptException exception) { + throw new TemplateException("Error while compiling script.", exception); + } + + // Create and a new template containing the original source + // and the compiled script: + template = new Template(source, script); + + // Update the cache: + cache.put(key, template); + + return template; + } + + /** + * Thi is equivalent to {@link #compile(String, TemplateOption...)} but it + * it accepts a variable list of options, thus making it simpler to use + * when options are hardcoded in the calling code. + */ + public Template compile(final String source, final TemplateOption... options) throws TemplateException { + final Set<TemplateOption> set = EnumSet.noneOf(TemplateOption.class); + for (final TemplateOption option : options) { + set.add(option); + } + return compile(source, set); + } +} diff --git a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateException.java b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateException.java new file mode 100644 index 0000000..75abd63 --- /dev/null +++ b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateException.java @@ -0,0 +1,13 @@ +package org.ovirt.engine.core.utils.templates; + +public class TemplateException extends Exception { + private static final long serialVersionUID = -5532073525436872546L; + + public TemplateException (String message, Throwable cause) { + super(message, cause); + } + + public TemplateException (String message) { + super(message); + } +} diff --git a/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateOption.java b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateOption.java new file mode 100644 index 0000000..a7a80c8 --- /dev/null +++ b/backend/manager/modules/utils/src/main/java/org/ovirt/engine/core/utils/templates/TemplateOption.java @@ -0,0 +1,35 @@ +package org.ovirt.engine.core.utils.templates; + +/** + * Options that can be passed to the template compiler to alter its + * behaviour. + */ +public enum TemplateOption { + /** + * If this OPTION is set all the white space inside the template will + * be ignored. This is useful when wanting to write templates that are + * readable but the output can't contain white space. For example, if you + * want to generate a template for an LDAP filter you can write something + * like this, using white space to make it readable: + * + * <pre> + * (& + * (dc=${domain}) + * (| + * <% for (id in ids) %> + * (uid=${id}) + * <% } %> + * ) + * ) + * </pre> + * + * But the result will be like this, without the white space: + * + * <pre> + * (&(dc=example.com)(|(uid=id1)(uid=id2))) + * </pre> + * + * The default is to preserve white space. + */ + IGNORE_WHITESPACE, +} diff --git a/backend/manager/modules/utils/src/test/java/org/ovirt/engine/core/utils/templates/TemplateTest.java b/backend/manager/modules/utils/src/test/java/org/ovirt/engine/core/utils/templates/TemplateTest.java new file mode 100644 index 0000000..3be572f --- /dev/null +++ b/backend/manager/modules/utils/src/test/java/org/ovirt/engine/core/utils/templates/TemplateTest.java @@ -0,0 +1,150 @@ +package org.ovirt.engine.core.utils.templates; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +public class TemplateTest { + + /** + * Test that the template can be evaluated correctly when no parameters + * are provided. + */ + @Test + public void testNullParameters() throws Exception { + testTemplate("empty", null); + } + + /** + * Test that if the same template is compiled twice in a row exactly + * the same template instance is returned from the cache. + */ + @Test + public void testCached() throws Exception { + String source = ""; + Template first = TemplateCompiler.getInstance().compile(source); + Template last = TemplateCompiler.getInstance().compile(source); + Assert.assertSame(first, last); + } + + /** + * Test that if the same template is compiled twice in a row with different + * source the instances returned from the cache are different. + */ + @Test + public void testNotCached() throws Exception { + Template first = TemplateCompiler.getInstance().compile("first"); + Template last = TemplateCompiler.getInstance().compile("second"); + Assert.assertNotSame(first, last); + } + + /** + * Test that if the same template is compiled twice in a row with different + * options the instances returned from the cache are different. + */ + @Test + public void testNotCachedDueToOptions() throws Exception { + String source = ""; + Template first = TemplateCompiler.getInstance().compile(source); + Template last = TemplateCompiler.getInstance().compile(source, TemplateOption.IGNORE_WHITESPACE); + Assert.assertNotSame(first, last); + } + + /** + * Test that the empty template produces a empty result. + */ + @Test + public void testEmpty() throws Exception { + Map<String, Object> parameters = Collections.emptyMap(); + testTemplate("empty", parameters); + } + + /** + * Test that comments are removed. + */ + @Test + public void testComments() throws Exception { + Map<String, Object> parameters = Collections.emptyMap(); + testTemplate("comments", parameters); + } + + /** + * Test a template that generates a simple LDAP filter. An important + * aspect of this test is that the template contains white space that + * must be ignored. + */ + @Test + public void testLdap() throws Exception { + Map<String, Object> parameters = new HashMap<>(); + parameters.put("domain", "example.com"); + int[] ids = new int[10]; + for (int i = 0; i < 10; i++) { + ids[i] = i; + } + parameters.put("ids", ids); + testTemplate("ldap", parameters, TemplateOption.IGNORE_WHITESPACE); + } + + /** + * This method loads a template and its expected result from resources, then + * evaluates it with the given parameters and checks that the result is + * equal to the expected result. + * + * @param name the name use to build the names of the resources adding the + * <code>.template</code> and <code>.expected</code> suffixes + * @param parameters the parameters for the template + * @param options the compilation options for the template + * @throws Exception if something fails during the compilation or + * evaluation + */ + public void testTemplate(String name, Map<String, Object> parameters, TemplateOption...options) throws Exception { + String source = getResource(name + ".template"); + String expected = getResource(name + ".expected"); + Template template = TemplateCompiler.getInstance().compile(source, options); + String actual = template.eval(parameters); + Assert.assertEquals(expected, actual); + } + + /** + * Loads an string from a resource. + * + * @param name the name of the resource, relative to the class + * @return a string with all the text contained in the resource + * @throws Exception if something fails while loading the resource + */ + private String getResource(String name) throws Exception { + URL url = TemplateTest.class.getResource(name); + if (url == null) { + throw new Exception("Can't find resource for name \"" + name + "\"."); + } + InputStream in = null; + ByteArrayOutputStream out = null; + try { + in = url.openStream(); + out = new ByteArrayOutputStream(1024); + byte[] buffer = new byte[1024]; + int count; + while ((count = in.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + } + finally { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } + if (out != null) { + return out.toString(); + } + return null; + } +} diff --git a/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.expected b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.expected new file mode 100644 index 0000000..0ae574e --- /dev/null +++ b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.expected @@ -0,0 +1,3 @@ +Some text. + +More text. \ No newline at end of file diff --git a/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.template b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.template new file mode 100644 index 0000000..06687d2 --- /dev/null +++ b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/comments.template @@ -0,0 +1,3 @@ +Some text. +<%-- A comment. --%> +More text. \ No newline at end of file diff --git a/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.expected b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.expected new file mode 100644 index 0000000..be5f36e --- /dev/null +++ b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.expected @@ -0,0 +1 @@ +(&(domain=EXAMPLE.COM)(|(uid=0)(uid=1)(uid=2)(uid=3)(uid=4)(uid=5)(uid=6)(uid=7)(uid=8)(uid=9))) \ No newline at end of file diff --git a/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.template b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.template new file mode 100644 index 0000000..b21c05c --- /dev/null +++ b/backend/manager/modules/utils/src/test/resources/org/ovirt/engine/core/utils/templates/ldap.template @@ -0,0 +1,8 @@ +(& + (domain=${domain.toUpperCase()}) + (| + <% for (id in ids) { %> + (uid=${id}) + <% } %> + ) +) \ No newline at end of file diff --git a/backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.expected b/backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.expected new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.expected diff --git a/backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.template b/backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.template new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/backend/manager/modules/utils/target/test-classes/org/ovirt/engine/core/utils/templates/empty.template -- To view, visit http://gerrit.ovirt.org/14650 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I14bf2a4d707002c5a1ddcd4ae1e7729957f359b8 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Juan Hernandez <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
