changeset 1252:4afcf1274915

Validator improvements. Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-September/008182.html Contributed-by: Giovanni Astarita
author Severin Gehwolf <sgehwolf@redhat.com>
date Fri, 06 Sep 2013 10:51:16 +0200
parents ff7b684c65ef
children 5501717515a7
files integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginConfigurationValidatorException.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginValidator.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationErrorsFormatter.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationIssue.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/AbstractValidationError.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/ConfigurationValidatorErrorHandler.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Error.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/FatalError.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Warning.java thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java validate-command/command/src/main/java/com/redhat/thermostat/validate/command/ValidateCommand.java validate-command/command/src/test/java/com/redhat/thermostat/validate/command/ValidateCommandTest.java validate-command/command/src/test/resources/correctPlugin.xml validate-command/command/src/test/resources/incorrectPlugin.xml validate-command/distribution/thermostat-plugin.xml
diffstat 18 files changed, 754 insertions(+), 207 deletions(-) [+]
line wrap: on
line diff
--- a/integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/PluginTest.java	Fri Sep 06 10:51:16 2013 +0200
@@ -121,22 +121,22 @@
                     "    <command>" +
                     "      <name>" + command + "</name>" +
                     "      <description>" + description + "</description>" +
-                    "      <environments>" +
-                    "        <environment>shell</environment>" +
-                    "        <environment>cli</environment>" +
-                    "      </environments>" +
                     "      <options>" +
                     "        <option>" +
                     "         <long>aaaaa</long>" +
                     "         <short>a</short>" +
                     "        </option>" +
                     "      </options>" +
+                    "      <environments>" +
+                    "        <environment>shell</environment>" +
+                    "        <environment>cli</environment>" +
+                    "      </environments>" +
                     "      <bundles>" +
-                    "        <bundle>bar</bundle>" +
+                    "        <bundle>" +
+                    "          <symbolic-name>bar</symbolic-name>" +
+                    "          <version>0.1.0</version>" +
+                    "        </bundle>" +
                     "      </bundles>" +
-                    "      <dependencies>" +
-                    "        <dependency>foo</dependency>" +
-                    "      </dependencies>" +
                     "    </command>" +
                     "  </commands>" +
                     "</plugin>";
@@ -191,11 +191,11 @@
                     "    <extension>" +
                     "      <name>" + command + "</name>" +
                     "      <bundles>" +
-                    "        <bundle>bar</bundle>" +
+                    "        <bundle>" +
+                    "          <symbolic-name>bar</symbolic-name>" +
+                    "          <version>0.1.0</version>" +
+                    "        </bundle>" +
                     "      </bundles>" +
-                    "      <dependencies>" +
-                    "        <dependency>foo</dependency>" +
-                    "      </dependencies>" +
                     "    </extension>" +
                     "  </extensions>" +
                     "</plugin>";
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java	Fri Sep 06 10:51:16 2013 +0200
@@ -40,7 +40,6 @@
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -55,6 +54,7 @@
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions;
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand;
 import com.redhat.thermostat.plugin.validator.PluginConfigurationValidatorException;
+import com.redhat.thermostat.plugin.validator.ValidationErrorsFormatter;
 
 /**
  * Searches for plugins under <code>$THERMOSTAT_HOME/plugins/</code> and
@@ -95,10 +95,15 @@
                 File configurationFile = new File(pluginDir, PLUGIN_CONFIG_FILE);
                 PluginConfiguration pluginConfig = parser.parse(configurationFile);
                 loadNewAndExtendedCommands(internalJarRoot, pluginDir, pluginConfig);
+                
             } catch (PluginConfigurationParseException exception) {
                 logger.log(Level.WARNING, "unable to parse plugin configuration", exception);
-            } catch (PluginConfigurationValidatorException exception) {
-                logger.log(Level.INFO, "unable to validate " + exception.getFilePath() + " file\n");
+                
+            } catch (PluginConfigurationValidatorException pcve) {
+                ValidationErrorsFormatter formatter = new ValidationErrorsFormatter();
+                logger.log(Level.INFO, formatter.format(pcve.getAllErrors()));
+                logger.log(Level.INFO, "unable to validate " + pcve.getXmlFile().getAbsolutePath() + " file\n");
+                
             } catch (FileNotFoundException exception) {
                 logger.log(Level.INFO, "file not found", exception);
             }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java	Fri Sep 06 10:51:16 2013 +0200
@@ -205,7 +205,7 @@
     
     public PluginConfiguration parse(File configurationFile) throws FileNotFoundException, PluginConfigurationValidatorException {
         PluginValidator validator = new PluginValidator();
-        validator.validate(configurationFile, false);
+        validator.validate(configurationFile);
         PluginConfiguration config = null;
         try (FileInputStream fis = new FileInputStream(configurationFile)) {
             config = parse(configurationFile.getParentFile().getName(), fis);
--- a/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginConfigurationValidatorException.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginConfigurationValidatorException.java	Fri Sep 06 10:51:16 2013 +0200
@@ -36,34 +36,113 @@
 
 package com.redhat.thermostat.plugin.validator;
 
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
+/**
+ * Thrown if there were schema validation issues or other problems in a
+ * thermostat-plugin.xml
+ * 
+ */
 public class PluginConfigurationValidatorException extends Exception {
-    
+
     private static final long serialVersionUID = 1L;
-    private String filePath;
-    
+    private final File xmlFile;
+    private final List<ValidationIssue> warnings;
+    private final List<ValidationIssue> errors;
+    private final List<ValidationIssue> fatals;
+
     /**
-     * Constructor of PluginConfigurationValidatorException
-     * @param filePath The absolute path to the file which failed to validate.
-     * @param message the detailed message
+     * Constructor.
+     * 
+     * Calls
+     * {@link #PluginConfigurationValidatorException(String, File, List, List, List, Throwable)}
+     * with a null cause.
      */
-    public PluginConfigurationValidatorException(String filePath, String message) {
-        super(message);
-        this.filePath = filePath;
+    public PluginConfigurationValidatorException(String message, File xmlFile,
+            List<ValidationIssue> errors, List<ValidationIssue> warnings,
+            List<ValidationIssue> fatals) {
+        this(message, xmlFile, errors, warnings, fatals, null);
     }
-    
+
     /**
-     * Constructor of PluginConfigurationValidatorException
-     * @param filePath The absolute path to the file which failed to validate.
-     * @param message the detailed message
+     * Constructor.
+     * 
+     * @param message
+     *            A descriptive message.
+     * @param xmlFile
+     *            The thermostat-plugin.xml file which caused this exception to
+     *            be thrown.
+     * @param errors
+     *            The list of schema validation errors.
+     * @param warnings
+     *            The list of (validation) warnings.
+     * @param fatals
+     *            The list of fatal (validation) errors.
+     * @param cause
+     *            The underlying exception. May be null.
+     */
+    public PluginConfigurationValidatorException(String message, File xmlFile,
+            List<ValidationIssue> errors, List<ValidationIssue> warnings,
+            List<ValidationIssue> fatals, Throwable cause) {
+        super(message);
+        this.xmlFile = xmlFile;
+        this.warnings = warnings;
+        this.errors = errors;
+        this.fatals = fatals;
+    }
+
+    /**
+     * 
+     * @return The list of all validation issues.
      */
-    public PluginConfigurationValidatorException(String filePath, String message, Throwable cause) {
-        super(message, cause);
-        this.filePath = filePath;
+    public List<ValidationIssue> getAllErrors() {
+        List<ValidationIssue> errorsList = new ArrayList<>();
+        errorsList.addAll(warnings);
+        errorsList.addAll(errors);
+        errorsList.addAll(fatals);
+        return errorsList;
+    }
+
+    /**
+     * 
+     * @return The thermostat-plugin.xml file which failed validation.
+     */
+    public File getXmlFile() {
+        return xmlFile;
+    }
+
+    /**
+     * Conditions that are not errors or fatal errors as defined by the XML
+     * recommendation.
+     * 
+     * @return The list of (validation) warnings.
+     */
+    public List<ValidationIssue> getWarnings() {
+        return warnings;
     }
-    
-    public String getFilePath() {
-        return filePath;
+
+    /**
+     * Each validation issue corresponds to the definition of "error" in section
+     * 1.2 of the W3C XML 1.0 Recommendation.
+     * 
+     * @return The list of violations of schema validity constraints.
+     */
+    public List<ValidationIssue> getErrors() {
+        return errors;
     }
-    
+
+    /**
+     * Each validation issue corresponds to the definition of "fatal error" in
+     * section 1.2 of the W3C XML 1.0 Recommendation.
+     * 
+     * @return The list of fatal (i.e. non-recoverable) errors. Fatal issues may
+     *         occur if the thermostat-plugin.xml violates well-formedness
+     *         constraints.
+     */
+    public List<ValidationIssue> getFatals() {
+        return fatals;
+    }
+
 }
--- a/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginValidator.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginValidator.java	Fri Sep 06 10:51:16 2013 +0200
@@ -52,27 +52,61 @@
 
 import com.redhat.thermostat.plugin.validator.internal.ConfigurationValidatorErrorHandler;
 
-
+/**
+ * Validates thermostat-plugin.xml files against its schema.
+ * 
+ */
 public class PluginValidator {
-
-    public void validate(File pluginXml, boolean isCommand) throws PluginConfigurationValidatorException, FileNotFoundException {
+    
+    /**
+     * The method validates a thermostat-plugin.xml file against the thermostat
+     * plug-in schema
+     * 
+     * @param pluginXml
+     *            The file to be validated
+     * @throws PluginConfigurationValidatorException
+     *             If any kind of schema validation error occurs. This exception
+     *             contains details about the issues which occurred. 
+     * @throws FileNotFoundException
+     *             if the file does not exist, is a directory rather than a
+     *             regular file, or for some other reason cannot be opened for
+     *             reading.
+     *             
+     *  @see PluginConfigurationValidatorException
+     *  @see ValidationErrorsFormatter
+     */
+    public void validate(File pluginXml) throws PluginConfigurationValidatorException, FileNotFoundException {
         URL schemaUrl = PluginValidator.class.getResource("/thermostat-plugin.xsd");
         SchemaFactory schemaFactory = 
                 SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        ConfigurationValidatorErrorHandler handler = new ConfigurationValidatorErrorHandler(pluginXml);
         
         try (FileInputStream fis = new FileInputStream(pluginXml)) {
             Schema schema = schemaFactory.newSchema(schemaUrl);
             Validator validator = schema.newValidator();
-            validator.setErrorHandler(new ConfigurationValidatorErrorHandler(pluginXml, isCommand));
+            validator.setErrorHandler(handler);
             validator.validate(new StreamSource(fis));
         } catch (SAXException exception) {
-            throw new PluginConfigurationValidatorException
-            (pluginXml.getAbsolutePath(), exception.getLocalizedMessage(), exception);
+            throw new PluginConfigurationValidatorException(
+                    "thermostat-plugin.xml not well-formed XML", pluginXml,
+                    handler.getErrors(), handler.getWarnings(),
+                    handler.getFatalErrors(), exception);
         } catch (FileNotFoundException fnfe) {
             throw fnfe;
         } catch (IOException ioe) {
-            throw new  PluginConfigurationValidatorException(ioe.getLocalizedMessage(), ioe.getMessage());
-        } 
+            throw new PluginConfigurationValidatorException(
+                    ioe.getLocalizedMessage(), pluginXml, handler.getErrors(),
+                    handler.getWarnings(), handler.getFatalErrors(), ioe);
+        }
+        
+        // We've registered an exception handler, so validation issues do not throw
+        // exceptions unless the xml file itself is not valid. Let's ask the handler
+        // if there were issues so we can throw an exception accordingly.
+        if (handler.hasValidationIssues()) {
+            throw new PluginConfigurationValidatorException(
+                    "Failed to validate thermostat-plugin.xml", pluginXml,
+                    handler.getErrors(), handler.getWarnings(),
+                    handler.getFatalErrors());
+        }
     }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationErrorsFormatter.java	Fri Sep 06 10:51:16 2013 +0200
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.plugin.validator;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.redhat.thermostat.plugin.validator.locale.LocaleResources;
+import com.redhat.thermostat.shared.locale.Translate;
+
+/**
+ * Formats XML validation issues to strings.
+ * 
+ * @see PluginConfigurationValidatorException
+ */
+public class ValidationErrorsFormatter {
+    
+    private enum ErrorType {
+        WARNING,
+        ERROR,
+        FATALERROR;
+    }
+
+    private Map<ErrorType,LocaleResources> translateKeys;
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+    
+    public ValidationErrorsFormatter() {
+        
+        translateKeys = new HashMap<>();
+        translateKeys.put(ErrorType.ERROR, LocaleResources.VALIDATION_ERROR);
+        translateKeys.put(ErrorType.WARNING, LocaleResources.VALIDATION_WARNING);
+        translateKeys.put(ErrorType.FATALERROR, LocaleResources.VALIDATION_FATAL_ERROR);
+        
+    }
+    
+    public String format(List<ValidationIssue> list) {
+        StringBuilder outputBuilder = new StringBuilder();
+        for (ValidationIssue ave : list) {
+            outputBuilder.append(formatError(ave));
+        }
+        return outputBuilder.toString();
+    }
+    
+    private StringBuilder formatError(ValidationIssue ave) {
+        StringBuilder builder = new StringBuilder();
+        
+        String LS = System.getProperty("line.separator");
+        String firstLine = null;
+        String secondLine = null;
+        String thirdLine = null;
+        String errorLine = null;
+        String pointer = "";
+        String absolutePath = ave.getXmlFile().getAbsolutePath();
+        
+        try {
+            BufferedReader br = new BufferedReader(new FileReader(absolutePath));
+            for (int i = 1; i < ave.getLineNumber()-3; i++) {
+                br.readLine();
+            }
+            firstLine = br.readLine();
+            secondLine = br.readLine();
+            thirdLine = br.readLine();
+            errorLine = br.readLine();
+            
+            for (int j = 1; j < ave.getColumnNumber()-1; j++) {
+                pointer = pointer.concat(" ");
+            }
+            pointer = pointer.concat("^");
+            br.close();
+        } catch (IOException exception) {
+            // if br fails to close
+        }
+        
+        builder.append(translator.localize(
+                       translateKeys.get(ErrorType.valueOf(ave.getClass().getSimpleName().toUpperCase())),
+                       absolutePath, 
+                       Integer.toString(ave.getLineNumber()), 
+                       Integer.toString(ave.getColumnNumber())).getContents());
+                    
+        builder.append(formatMessage(ave.getMessage())).append(LS).append(LS);
+        builder.append(firstLine).append(LS);
+        builder.append(secondLine).append(LS);
+        builder.append(thirdLine).append(LS);
+        builder.append(errorLine).append(LS);
+        builder.append(pointer).append(LS);
+        
+        return builder;
+    }
+    
+    private String formatMessage(String message) {
+        String[] arguments = message.split("\"http://icedtea.classpath.org/thermostat/plugins/v1.0\":");
+        int size = arguments.length;
+        String output = "";
+        
+        for (int i = 0; i < size; i++) {
+            output=output.concat(arguments[i]);
+        }
+        return output;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/ValidationIssue.java	Fri Sep 06 10:51:16 2013 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.plugin.validator;
+
+import java.io.File;
+
+/**
+ * Represents XML schema validation issues.
+ * 
+ */
+public interface ValidationIssue {
+
+    /**
+     * 
+     * @return The line number where the issue occurred.
+     */
+    public int getLineNumber();
+
+    /**
+     * 
+     * @return The column number where the issue occurred.
+     */
+    public int getColumnNumber();
+
+    /**
+     * 
+     * @return A message describing the issue.
+     */
+    public String getMessage();
+
+    /**
+     * 
+     * @return The thermostat-plugin.xml file which caused the issue.
+     */
+    public File getXmlFile();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/AbstractValidationError.java	Fri Sep 06 10:51:16 2013 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.plugin.validator.internal;
+
+import java.io.File;
+
+import com.redhat.thermostat.plugin.validator.ValidationIssue;
+
+/**
+ * 
+ * Exception thrown on XML validation errors of thermostat-plugin.xml files.
+ *
+ */
+public abstract class AbstractValidationError implements ValidationIssue {
+
+    private int lineNumber;
+    private int columnNumber;
+    private String message;
+    private File xmlFile;
+    
+    public AbstractValidationError(int lineNumber, int columnNumber, String message, File xmlFile) {
+        this.lineNumber = lineNumber;
+        this.columnNumber = columnNumber;
+        this.message = message;
+        this.xmlFile = xmlFile;
+    }
+
+    @Override
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    @Override
+    public int getColumnNumber() {
+        return columnNumber;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public File getXmlFile() {
+        return xmlFile;
+    }
+    
+    public String toString() {
+        String LS = System.getProperty("line.separator");
+        StringBuilder builder = new StringBuilder();
+        
+        builder.append("[").append(getName()).append("]").append(LS);
+        builder.append("   Message: ").append(getMessage()).append(LS);
+        builder.append("   File: ").append(getXmlFile().getPath()).append(LS);
+        builder.append("   Line number: ").append(getLineNumber()).append(LS);
+        builder.append("   Column number: ").append(getColumnNumber()).append(LS);
+        
+        return builder.toString();
+        
+    }
+
+    public abstract String getName();
+
+}
--- a/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/ConfigurationValidatorErrorHandler.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/ConfigurationValidatorErrorHandler.java	Fri Sep 06 10:51:16 2013 +0200
@@ -36,127 +36,77 @@
 
 package com.redhat.thermostat.plugin.validator.internal;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Logger;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 
-import com.redhat.thermostat.plugin.validator.locale.LocaleResources;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.plugin.validator.ValidationIssue;
 
 public class ConfigurationValidatorErrorHandler implements ErrorHandler {
 
-    private static final Logger logger = LoggingUtils.getLogger(ConfigurationValidatorErrorHandler.class);
-    private int warningsErrorsCounter = 0;
-    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
     private final File pluginXML;
-    private boolean isCommand;
-    
-    private enum ErrorType {
-        WARNING,
-        ERROR,
-        FATAL_ERROR;
-    }
-    
-    public ConfigurationValidatorErrorHandler(File pluginXML, boolean isCommand) {
+    private final List<ValidationIssue> warnings;
+    private final List<ValidationIssue> errors;
+    private final List<ValidationIssue> fatalErrors;
+
+    public ConfigurationValidatorErrorHandler(File pluginXML) {
         this.pluginXML = pluginXML;
-        this.isCommand = isCommand;
+        warnings = new ArrayList<>();
+        errors = new ArrayList<>();
+        fatalErrors = new ArrayList<>();
     }
 
     @Override
     public void warning(SAXParseException exception) throws SAXException {
-        warningsErrorsCounter++;
-        printInfo(exception, ErrorType.WARNING);
+        Warning newWarning = new Warning(exception.getLineNumber(), 
+                                         exception.getColumnNumber(),
+                                         exception.getLocalizedMessage(),
+                                         pluginXML);
+        warnings.add(newWarning);
     }
 
     @Override
     public void error(SAXParseException exception) throws SAXParseException {
-        warningsErrorsCounter++;
-        printInfo(exception, ErrorType.ERROR);
+        Error newError = new Error(exception.getLineNumber(), 
+                                   exception.getColumnNumber(),
+                                   exception.getLocalizedMessage(),
+                                   pluginXML);
+        errors.add(newError);
     }
     
     @Override
     public void fatalError(SAXParseException exception) throws SAXParseException {
-        if (warningsErrorsCounter == 0) {
-            printInfo(exception, ErrorType.FATAL_ERROR);
-            logger.info("XML not well formed");
+        //  Fatal errors will be reported just when no validation warnings and errors happened. 
+        //  In this way we avoid wrong messages of bad form for files that have wrong tags not closed properly
+        if (errors.size() == 0 && warnings.size() == 0) {
+            FatalError newFatalError = new FatalError(exception.getLineNumber(), 
+                                                      exception.getColumnNumber(),
+                                                      exception.getLocalizedMessage(),
+                                                      pluginXML);
+            fatalErrors.add(newFatalError);
         }
     }
 
-    private void printInfo(SAXParseException e, ErrorType type) {
-        int columnNumber = e.getColumnNumber();
-        int lineNumber = e.getLineNumber();
-        
-        StringBuilder builder = new StringBuilder();
-        
-        String LS = System.getProperty("line.separator");
-        String firstLine = null;
-        String secondLine = null;
-        String thirdLine = null;
-        String errorLine = null;
-        String pointer = "";
-        String absolutePath = pluginXML.getAbsolutePath();
-        
-        Map<ErrorType,LocaleResources> translateKeys = new HashMap<>();
-        translateKeys.put(ErrorType.ERROR, LocaleResources.VALIDATION_ERROR);
-        translateKeys.put(ErrorType.WARNING, LocaleResources.VALIDATION_WARNING);
-        translateKeys.put(ErrorType.FATAL_ERROR, LocaleResources.VALIDATION_FATAL_ERROR);
-        
-        try {
-            BufferedReader br = new BufferedReader(new FileReader(absolutePath));
-            for (int i = 1; i < lineNumber-3; i++) {
-                br.readLine();
-            }
-            firstLine = br.readLine();
-            secondLine = br.readLine();
-            thirdLine = br.readLine();
-            errorLine = br.readLine();
-            
-            for (int j = 1; j < columnNumber-1; j++) {
-                pointer = pointer.concat(" ");
-            }
-            pointer = pointer.concat("^");
-            br.close();
-        } catch (IOException exception) {
-            // if br fails to close
-        }
-        
-        builder.append(translator.localize(
-                translateKeys.get(type),
-                absolutePath, 
-                Integer.toString(lineNumber), 
-                Integer.toString(columnNumber)).getContents());
-                    
-        builder.append(formatMessage(e.getLocalizedMessage())).append(LS).append(LS);
-        builder.append(firstLine).append(LS);
-        builder.append(secondLine).append(LS);
-        builder.append(thirdLine).append(LS);
-        builder.append(errorLine).append(LS);
-        builder.append(pointer).append(LS);
-        
-        if (isCommand) {
-            System.out.println(builder.toString());
-        } else {
-            logger.info(builder.toString());
-        }
-        
+    public List<ValidationIssue> getWarnings() {
+        return warnings;
+    }
+
+    public List<ValidationIssue> getErrors() {
+        return errors;
     }
-    
-    private static String formatMessage(String message) {
-        String[] arguments = message.split("\"http://icedtea.classpath.org/thermostat/plugins/v1.0\":");
-        int size = arguments.length;
-        String output = "";
-        for (int i = 0; i < size; i++) {
-            output=output.concat(arguments[i]);
-        }
-        return output;
+
+    public List<ValidationIssue> getFatalErrors() {
+        return fatalErrors;
     }
+
+    public boolean hasValidationIssues() {
+        return errors.size() > 0 ||
+                warnings.size() > 0 ||
+                fatalErrors.size() > 0;
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Error.java	Fri Sep 06 10:51:16 2013 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.plugin.validator.internal;
+
+import java.io.File;
+
+class Error extends AbstractValidationError {
+
+	Error(int lineNumber, int columnNumber, String message, File xmlFile) {
+		super(lineNumber, columnNumber, message, xmlFile);
+	}
+
+	@Override
+	public String getName() {
+		return this.getClass().getSimpleName();
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/FatalError.java	Fri Sep 06 10:51:16 2013 +0200
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.plugin.validator.internal;
+
+import java.io.File;
+
+class FatalError extends AbstractValidationError {
+
+	FatalError(int lineNumber, int columnNumber, String message, File xmlFile) {
+		super(lineNumber, columnNumber, message, xmlFile);
+	}
+
+	@Override
+	public String getName() {
+		return this.getClass().getSimpleName();
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/Warning.java	Fri Sep 06 10:51:16 2013 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.plugin.validator.internal;
+
+import java.io.File;
+
+class Warning extends AbstractValidationError {
+
+	Warning(int lineNumber, int columnNumber, String message, File xmlFile) {
+		super(lineNumber, columnNumber, message, xmlFile);
+	}
+
+	@Override
+	public String getName() {
+		return this.getClass().getSimpleName();
+	}
+
+}
\ No newline at end of file
--- a/thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java	Fri Sep 06 10:51:16 2013 +0200
@@ -37,7 +37,7 @@
 package com.redhat.thermostat.plugin.validator;
 
 
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -46,15 +46,16 @@
 
 import org.junit.Test;
 
+
 public class PluginValidatorTest {
-
+    
     @Test
     public void validateEmptyConfiguration() throws IOException {
         String config = "<?xml version=\"1.0\"?>\n";
         File testFile = createFile("testSystemId", config);
         PluginValidator validator = new PluginValidator();
         try {
-            validator.validate(testFile, false);
+            validator.validate(testFile);
             fail("should not come here");
         } catch (PluginConfigurationValidatorException e) {
             //pass
@@ -75,43 +76,20 @@
                     "    <extension>\n" +
                     "      <name>test</name>\n" +
                     "      <bundles>\n" +
-                    "        <bundle>foo</bundle>\n" +
-                    "        <bundle>bar</bundle>\n" +
-                    "        <bundle>baz</bundle>\n" +
+                    "        <bundle><symbolic-name>foo</symbolic-name><version>1</version></bundle>\n" +
+                    "        <bundle><symbolic-name>bar</symbolic-name><version>2</version></bundle>\n" +
+                    "        <bundle><symbolic-name>baz</symbolic-name><version>3</version></bundle>\n" +
                     "      </bundles>\n" +
-                    "      <dependencies>\n" +
-                    "        <dependency>thermostat-foo</dependency>\n" +
-                    "      </dependencies>\n" +
                     "    </extension>\n" +
                     "  </extensions>\n" +
                     "</plugin>";
             File testFile = createFile("testSystemId", config);
             PluginValidator validator = new PluginValidator();
-            validator.validate(testFile, false);
+            validator.validate(testFile);
+            
+            //  Second validation on the same file
+            validator.validate(testFile);
             testFile.delete();
-        
-            config = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
-                    "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" +
-                    " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
-                    " xsi:schemaLocation=\"http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd\">\n" +
-                    "  <extensions>\n" +
-                    "    <extension>\n" +
-                    "      <name>test</name>\n" +
-                    "      <bundles>\n" +
-                    "        <bundle>foo</bundle>\n" +
-                    "        <bundle>bar</bundle>\n" +
-                    "        <bundle>baz</bundle>\n" +
-                    "      </bundles>\n" +
-                    "      <dependencies>\n" +
-                    "        <dependency>thermostat-foo</dependency>\n" +
-                    "      </dependencies>\n" +
-                    "    </extension>\n" +
-                    "  </extensions>\n" +
-                    "</plugin>";
-            File testFile2 = createFile("testSystemId", config);
-            PluginValidator validator2 = new PluginValidator();
-            validator2.validate(testFile, false);
-            testFile2.delete();
         } catch (PluginConfigurationValidatorException e) {
            fail("should not reach here, plugin.xml should be validated according to schema");
         }
@@ -123,18 +101,18 @@
                 "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" +
                 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
                 " xsi:schemaLocation=\"http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd\">\n" +
-                "  <extensions>\n" +
+                "  <extensions>\n" +      
                 "    <something>\n" +    // Error line
                 "    <extension>\n" +
                 "      <name>test</name>\n" +
                 "      <bundles>\n" +
-                "        <bundle>foo</bundle>\n" +
-                "        <bundle>bar</bundle>\n" +
-                "        <bundle>baz</bundle>\n" +
+                "        <bundle>foo</bundle>\n" +   // Error line
+                "        <bundle><symbolic-name>bar</symbolic-name><version>2</version></bundle>\n" +
+                "        <bundle><symbolic-name>baz</symbolic-name></bundle>\n" +   // Error line
                 "      </bundles>\n" +
-                "      <dependencies>\n" +
-                "        <dependency>thermostat-foo</dependency>\n" +
-                "      </dependencies>\n" +
+                "      <dependencies>\n" +   // Error line
+                "        <dependency>thermostat-foo</dependency>\n" +   // Error line
+                "      </dependencies>\n" +   // Error line
                 "    </extension>\n" +
                 "  </extensions>\n" +
                 "</plugin>";
@@ -142,7 +120,7 @@
         File testFile = createFile("testSystemId", config);
         PluginValidator validator = new PluginValidator();
         try {
-            validator.validate(testFile, false);
+            validator.validate(testFile);
             fail("plugin.xml should not validate according to schema");
         } catch (PluginConfigurationValidatorException e) {
             //pass
@@ -191,13 +169,10 @@
                 "        <environment>cli</environment>\n" +
                 "      </environments>\n" +
                 "      <bundles>\n" +
-                "        <bundle>\n \t  \nfoo\t \n \n</bundle>\n" +
-                "        <bundle>\tbar  baz\n</bundle>\n" +
-                "        <bundle>buzz</bundle>\n" +
+                "        <bundle><symbolic-name>foo</symbolic-name><version>1</version></bundle>\n" +
+                "        <bundle><symbolic-name>bar</symbolic-name><version>2</version></bundle>\n" +
+                "        <bundle><symbolic-name>baz</symbolic-name><version>3</version></bundle>\n" +
                 "      </bundles>\n" +
-                "      <dependencies>\n\t\n\t \t\t\n" +
-                "        <dependency>\t\t\t  thermostat-foo\n\t\t\n</dependency>\n" +
-                "      </dependencies>\n" +
                 "    </command>\n" +
                 "  </commands>\n" +
                 "</plugin>";
@@ -205,9 +180,9 @@
         File testFile = createFile("testSystemId", config);
         PluginValidator validator = new PluginValidator();
         try {
-            validator.validate(testFile, false);
+            validator.validate(testFile);
         } catch (PluginConfigurationValidatorException e) {
-           fail("should not reach here, plugin.xml should be validated according to schema");
+            fail("should not reach here, plugin.xml should be validated according to schema");
         } finally {
             testFile.delete();
         }
--- a/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/ValidateCommand.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/validate-command/command/src/main/java/com/redhat/thermostat/validate/command/ValidateCommand.java	Fri Sep 06 10:51:16 2013 +0200
@@ -38,7 +38,6 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.IOException;
 
 import com.redhat.thermostat.common.cli.Arguments;
 import com.redhat.thermostat.common.cli.Command;
@@ -47,28 +46,32 @@
 import com.redhat.thermostat.common.cli.CommandLineArgumentParseException;
 import com.redhat.thermostat.plugin.validator.PluginConfigurationValidatorException;
 import com.redhat.thermostat.plugin.validator.PluginValidator;
+import com.redhat.thermostat.plugin.validator.ValidationErrorsFormatter;
 import com.redhat.thermostat.shared.locale.Translate;
 import com.redhat.thermostat.validate.command.locale.LocaleResources;
 
 public class ValidateCommand implements Command {
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+    private PluginValidator validator;
 
     public void run(CommandContext ctx) throws CommandException {
         Arguments args = ctx.getArguments();
-        PluginValidator validator = new PluginValidator();
+        validator = new PluginValidator();
         File pluginFile = null;
         String argString = null;
         
             try {
                 argString = args.getNonOptionArguments().get(0);
                 pluginFile = new File(argString);
-                validator.validate(pluginFile, true);
+                validator.validate(pluginFile);
                 ctx.getConsole().getOutput().println(translator.localize(
                                 LocaleResources.VALIDATION_SUCCESSFUL, pluginFile.getAbsolutePath())
                                 .getContents());
                 
             } catch (PluginConfigurationValidatorException e) {
+                ValidationErrorsFormatter formatter = new ValidationErrorsFormatter();
+                ctx.getConsole().getError().println(formatter.format(e.getAllErrors()));
                 ctx.getConsole().getError().println(translator.localize(
                                 LocaleResources.VALIDATION_FAILED, pluginFile.getAbsolutePath())
                                .getContents());
--- a/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/ValidateCommandTest.java	Wed Sep 11 12:44:35 2013 -0400
+++ b/validate-command/command/src/test/java/com/redhat/thermostat/validate/command/ValidateCommandTest.java	Fri Sep 06 10:51:16 2013 +0200
@@ -36,7 +36,8 @@
 
 package com.redhat.thermostat.validate.command;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -95,11 +96,10 @@
         list.add(fileName);
         
         cmd.run(ctxt);
+        String errorOutput = new String(errorBaos.toByteArray());
+        String validateOutput = buildErrorMessage();
         
-        String actual = new String(errorBaos.toByteArray());
-        String expected = "Validation failed for file " + fileName + "\n\n";
-        assertEquals(expected, actual);
-
+        assertEquals(validateOutput, errorOutput);
         assertEquals("", new String(outputBaos.toByteArray()));
     }
 
@@ -143,5 +143,47 @@
     public void testStorageRequired() {
         assertFalse(cmd.isStorageRequired());
     }
+    
+    private String buildErrorMessage() {
+        String LS = System.getProperty("line.separator");
+        StringBuilder builder = new StringBuilder();
+        
+        
+        builder.append("Error in file ").append(fileName).append(":10.60").append(LS);
+        builder.append("cvc-complex-type.2.4.b: The content of element 'bundle' is not complete. One of '{version}' is expected.").append(LS).append(LS);
+
+        builder.append("      <name>test</name>").append(LS);
+        builder.append("      <bundles>").append(LS);
+        builder.append("        <bundle><symbolic-name>foo</symbolic-name><version>1</version></bundle>").append(LS);
+        builder.append("        <bundle><symbolic-name>bar</symbolic-name></bundle>").append(LS);
+        builder.append("                                                          ^").append(LS);
+        builder.append("Error in file ").append(fileName).append(":11.29").append(LS);
+        builder.append("cvc-complex-type.2.3: Element 'bundle' cannot have character [children], because the type's content type is element-only.").append(LS).append(LS);
+
+        builder.append("      <bundles>").append(LS);
+        builder.append("        <bundle><symbolic-name>foo</symbolic-name><version>1</version></bundle>").append(LS);
+        builder.append("        <bundle><symbolic-name>bar</symbolic-name></bundle>").append(LS);
+        builder.append("        <bundle>baz</bundle>").append(LS);
+        builder.append("                           ^").append(LS);
+        builder.append("Error in file ").append(fileName).append(":11.29").append(LS);
+        builder.append("cvc-complex-type.2.4.b: The content of element 'bundle' is not complete. One of '{symbolic-name}' is expected.").append(LS).append(LS);
+
+        builder.append("      <bundles>").append(LS);
+        builder.append("        <bundle><symbolic-name>foo</symbolic-name><version>1</version></bundle>").append(LS);
+        builder.append("        <bundle><symbolic-name>bar</symbolic-name></bundle>").append(LS);
+        builder.append("        <bundle>baz</bundle>").append(LS);
+        builder.append("                           ^").append(LS);
+        builder.append("Error in file ").append(fileName).append(":13.21").append(LS);
+        builder.append("cvc-complex-type.2.4.d: Invalid content was found starting with element 'dependencies'. No child element is expected at this point.").append(LS).append(LS);
+
+        builder.append("        <bundle><symbolic-name>bar</symbolic-name></bundle>").append(LS);
+        builder.append("        <bundle>baz</bundle>").append(LS);
+        builder.append("      </bundles>").append(LS);
+        builder.append("      <dependencies>").append(LS);
+        builder.append("                   ^").append(LS).append(LS);
+        builder.append("Validation failed for file ").append(fileName).append(LS).append(LS);
+
+        return builder.toString();
+    }
 
 }
--- a/validate-command/command/src/test/resources/correctPlugin.xml	Wed Sep 11 12:44:35 2013 -0400
+++ b/validate-command/command/src/test/resources/correctPlugin.xml	Fri Sep 06 10:51:16 2013 +0200
@@ -6,13 +6,10 @@
     <extension>
       <name>test</name>
       <bundles>
-        <bundle>foo</bundle>
-        <bundle>bar</bundle>
-        <bundle>baz</bundle>
+        <bundle><symbolic-name>foo</symbolic-name><version>1</version></bundle>
+        <bundle><symbolic-name>bar</symbolic-name><version>2</version></bundle>
+        <bundle><symbolic-name>baz</symbolic-name><version>3</version></bundle>
       </bundles>
-      <dependencies>
-        <dependency>thermostat-foo</dependency>
-      </dependencies>
     </extension>
   </extensions>
 </plugin>
--- a/validate-command/command/src/test/resources/incorrectPlugin.xml	Wed Sep 11 12:44:35 2013 -0400
+++ b/validate-command/command/src/test/resources/incorrectPlugin.xml	Fri Sep 06 10:51:16 2013 +0200
@@ -3,12 +3,11 @@
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 thermostat-plugin.xsd">
   <extensions>
-    <something>
     <extension>
       <name>test</name>
       <bundles>
-        <bundle>foo</bundle>
-        <bundle>bar</bundle>
+        <bundle><symbolic-name>foo</symbolic-name><version>1</version></bundle>
+        <bundle><symbolic-name>bar</symbolic-name></bundle>
         <bundle>baz</bundle>
       </bundles>
       <dependencies>
--- a/validate-command/distribution/thermostat-plugin.xml	Wed Sep 11 12:44:35 2013 -0400
+++ b/validate-command/distribution/thermostat-plugin.xml	Fri Sep 06 10:51:16 2013 +0200
@@ -53,7 +53,7 @@
       <bundles>
         <bundle><symbolic-name>com.redhat.thermostat.validate.command</symbolic-name><version>${project.version}</version></bundle>
         <bundle><symbolic-name>com.redhat.thermostat.common.core</symbolic-name><version>${project.version}</version></bundle>
-        <bundle><symbolic-name>com.redhat.thermostat.shared.config</symbolic-name><version>${project.version}</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.configuration</symbolic-name><version>${project.version}</version></bundle>
         <bundle><symbolic-name>com.redhat.thermostat.plugin.validator</symbolic-name><version>${project.version}</version></bundle>
       </bundles>
     </command>