changeset 1129:aa48ed4752c1

Plugin validator as separate module Reviewed-by: neugens, jerboaa, omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-May/006874.html
author Giovanni Astarita <gastarit@redhat.com>
date Wed, 05 Jun 2013 11:14:27 +0200
parents a746c19af58f
children bb4bde056ead
files launcher/pom.xml launcher/src/main/java/com/redhat/thermostat/launcher/internal/LocaleResources.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationValidatorException.java launcher/src/main/resources/com/redhat/thermostat/launcher/internal/strings.properties launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties pom.xml thermostat-plugin-validator/pom.xml 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/internal/ConfigurationValidatorErrorHandler.java thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/locale/LocaleResources.java thermostat-plugin-validator/src/main/resources/com/redhat/thermostat/plugin/validator/strings.properties thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/locale/LocaleResourcesTest.java
diffstat 18 files changed, 766 insertions(+), 400 deletions(-) [+]
line wrap: on
line diff
--- a/launcher/pom.xml	Mon Jun 03 18:10:51 2013 +0200
+++ b/launcher/pom.xml	Wed Jun 05 11:14:27 2013 +0200
@@ -53,23 +53,6 @@
   
 
   <build>
-    <resources>
-      <!-- Instructions that include thermostat plugin schema into the launcher jar -->
-      <resource>
-        <directory>${basedir}/../distribution/docs/</directory>
-        <includes>
-          <include>thermostat-plugin.xsd</include>
-        </includes>
-      </resource>
-      <!-- Instructions are not cumulative to default behaviour, 
-           so it's necessary to add default behaviour instructions after explicit previous ones -->
-      <resource>
-        <directory>src/main/resources/</directory>
-        <includes>
-          <include>**/*</include>
-        </includes>
-      </resource>
-    </resources>
     <plugins>
       <plugin>
         <groupId>org.apache.felix</groupId>
@@ -136,6 +119,11 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-plugin-validator</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 
 </project>
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LocaleResources.java	Mon Jun 03 18:10:51 2013 +0200
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LocaleResources.java	Wed Jun 05 11:14:27 2013 +0200
@@ -59,10 +59,6 @@
     LAUNCHER_USER_AUTH_PROMPT_ERROR,
     LAUNCHER_MALFORMED_URL,
     LAUNCHER_CONNECTION_ERROR,
-
-    VALIDATION_WARNING,
-    VALIDATION_ERROR,
-    VALIDATION_FATAL_ERROR,
     ;
 
     static final String RESOURCE_BUNDLE = "com.redhat.thermostat.launcher.internal.strings";
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java	Mon Jun 03 18:10:51 2013 +0200
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java	Wed Jun 05 11:14:27 2013 +0200
@@ -52,6 +52,7 @@
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions;
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand;
+import com.redhat.thermostat.plugin.validator.PluginConfigurationValidatorException;
 
 /**
  * Searches for plugins under <code>$THERMOSTAT_HOME/plugins/</code> and
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java	Mon Jun 03 18:10:51 2013 +0200
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java	Wed Jun 05 11:14:27 2013 +0200
@@ -37,21 +37,16 @@
 package com.redhat.thermostat.launcher.internal;
 
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.logging.Logger;
 
@@ -59,10 +54,6 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
-import javax.xml.XMLConstants;
 
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.OptionGroup;
@@ -78,7 +69,8 @@
 import com.redhat.thermostat.launcher.internal.CommandInfo.Environment;
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions;
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand;
-import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.plugin.validator.PluginConfigurationValidatorException;
+import com.redhat.thermostat.plugin.validator.PluginValidator;
 
 /**
  * Parses the configuration of a plugin as specified in an {@code File} or an
@@ -207,25 +199,10 @@
     // thread safe because there is no state :)
     
     public PluginConfiguration parse(File configurationFile) throws FileNotFoundException, PluginConfigurationValidatorException {
-        validate(new StreamSource(configurationFile));
+        PluginValidator validator = new PluginValidator();
+        validator.validate(new StreamSource(configurationFile));
         return parse(configurationFile.getParentFile().getName(), new FileInputStream(configurationFile));
     }
-           
-    void validate(StreamSource plugin) throws PluginConfigurationValidatorException {
-        URL schemaUrl = getClass().getResource("/thermostat-plugin.xsd");
-        SchemaFactory schemaFactory = 
-                SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        
-        try {
-            Schema schema = schemaFactory.newSchema(schemaUrl);
-            Validator validator = schema.newValidator();
-            validator.setErrorHandler(new ConfigurationValidatorErrorHandler());
-            validator.validate(plugin);
-        } catch (IOException | SAXException exception) {
-            throw new PluginConfigurationValidatorException
-                (plugin.getSystemId(), exception.getLocalizedMessage(), exception);
-        }
-    }
     
     PluginConfiguration parse(String pluginName, InputStream configurationStream) {
         try {
@@ -553,100 +530,4 @@
         }
     }
     
-    private static class ConfigurationValidatorErrorHandler implements ErrorHandler {
-        
-        private int warningsErrorsCounter = 0;
-        private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
-        
-        private enum ErrorType {
-            WARNING,
-            ERROR,
-            FATAL_ERROR;
-        }
-
-        @Override
-        public void warning(SAXParseException exception) throws SAXException {
-            warningsErrorsCounter++;
-            printInfo(exception, ErrorType.WARNING);
-        }
-
-        @Override
-        public void error(SAXParseException exception) throws SAXParseException {
-            warningsErrorsCounter++;
-            printInfo(exception, ErrorType.ERROR);
-        }
-        
-        @Override
-        public void fatalError(SAXParseException exception) throws SAXParseException {
-            if (warningsErrorsCounter == 0) {
-                printInfo(exception, ErrorType.FATAL_ERROR);
-                logger.warning("XML not well formed");
-            }
-        }
-
-        private static void printInfo(SAXParseException e, ErrorType type) {
-            int columnNumber = e.getColumnNumber();
-            int lineNumber = e.getLineNumber();
-            
-            StringBuffer buffer = new StringBuffer();
-            
-            String firstLine = null;
-            String secondLine = null;
-            String thirdLine = null;
-            String errorLine = null;
-            String pointer = "";
-            String absolutePath = e.getSystemId();
-            absolutePath = absolutePath.substring(5);
-            
-            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) {
-                System.out.println("File not found!");;
-            }
-            
-            buffer.append(translator.localize(
-                    translateKeys.get(type),
-                    absolutePath, 
-                    Integer.toString(lineNumber), 
-                    Integer.toString(columnNumber)).getContents());
-                        
-            buffer.append(formatMessage(e.getLocalizedMessage()) + "\n\n");
-            buffer.append(firstLine + "\n");
-            buffer.append(secondLine + "\n");
-            buffer.append(thirdLine + "\n");
-            buffer.append(errorLine + "\n");
-            buffer.append(pointer  + "\n");
-            
-            logger.warning("\n" + buffer.toString());
-        }
-        
-        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;
-        }
-    }
-    
 }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationValidatorException.java	Mon Jun 03 18:10:51 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * 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.launcher.internal;
-
-
-public class PluginConfigurationValidatorException extends Exception {
-    
-    private static final long serialVersionUID = 1L;
-    private String filePath;
-    
-    /**
-     * Constructor of PluginConfigurationValidatorException
-     * @param filePath must include the protocol 
-     * @param message the detailed message
-     */
-    public PluginConfigurationValidatorException(String filePath, String message) {
-        super(message);
-        this.filePath = computeFilePath(filePath);
-    }
-    
-    /**
-     * Constructor of PluginConfigurationValidatorException
-     * @param filePath must include the protocol 
-     * @param message the detailed message
-     */
-    public PluginConfigurationValidatorException(String filePath, String message, Throwable cause) {
-        super(message, cause);
-        this.filePath = computeFilePath(filePath);
-    }
-    
-    public String getFilePath() {
-        return filePath;
-    }
-    
-    /**
-     * Computes the file path removing the protocol scheme
-     * @param filePath must include the protocol
-     * @return the path without the protocol scheme
-     */
-    private String computeFilePath(String filePath) {
-        // the substring starts from position 5, skipping "file:" filePath content 
-        return filePath.substring(5);
-    }
-
-}
--- a/launcher/src/main/resources/com/redhat/thermostat/launcher/internal/strings.properties	Mon Jun 03 18:10:51 2013 +0200
+++ b/launcher/src/main/resources/com/redhat/thermostat/launcher/internal/strings.properties	Wed Jun 05 11:14:27 2013 +0200
@@ -18,8 +18,4 @@
 
 LAUNCHER_USER_AUTH_PROMPT_ERROR = Error while prompting for username and password.
 LAUNCHER_MALFORMED_URL = Unsupported storage URL: {0}
-LAUNCHER_CONNECTION_ERROR = Could not connect to: {0}
-
-VALIDATION_WARNING = Warning in file {0}:{1}.{2}\n
-VALIDATION_ERROR = Error in file {0}:{1}.{2}\n
-VALIDATION_FATAL_ERROR = Fatal error in file {0}:{1}.{2}\n
\ No newline at end of file
+LAUNCHER_CONNECTION_ERROR = Could not connect to: {0}
\ No newline at end of file
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java	Mon Jun 03 18:10:51 2013 +0200
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java	Wed Jun 05 11:14:27 2013 +0200
@@ -66,6 +66,7 @@
 import com.redhat.thermostat.launcher.internal.CommandInfo.Environment;
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions;
 import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand;
+import com.redhat.thermostat.plugin.validator.PluginConfigurationValidatorException;
 
 public class PluginCommandInfoSourceTest {
 
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java	Mon Jun 03 18:10:51 2013 +0200
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java	Wed Jun 05 11:14:27 2013 +0200
@@ -76,21 +76,6 @@
     }
     
     @Test
-    public void validateEmptyConfiguration() throws IOException {
-        String config = "<?xml version=\"1.0\"?>\n";
-        PluginConfigurationParser parser = new PluginConfigurationParser();
-        File testFile = createFile("testSystemId", config);
-        try {
-            parser.validate(new StreamSource(testFile));
-            fail("should not come here");
-        } catch (PluginConfigurationValidatorException e) {
-            //pass
-        } finally {
-            testFile.delete();
-        }
-    }
-
-    @Test
     public void testMinimalConfiguration() throws UnsupportedEncodingException {
         PluginConfigurationParser parser = new PluginConfigurationParser();
         String config = "" +
@@ -141,93 +126,6 @@
     }
     
     @Test
-    public void canValidatePluginXMLMultipleTimes() throws Exception {
-        
-        try {
-            String 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>";
-            PluginConfigurationParser parser = new PluginConfigurationParser();
-            File testFile = createFile("testSystemId", config);
-            parser.validate(new StreamSource(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);
-            parser.validate(new StreamSource(testFile));
-            testFile2.delete();
-        } catch (PluginConfigurationValidatorException e) {
-           fail("should not reach here, plugin.xml should be validated according to schema");
-        }
-    }
-    
-    @Test
-    public void validationFailsOnInvalidPluginFile() throws Exception {
-        String 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" +
-                "    <something>\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>";
-
-        PluginConfigurationParser parser = new PluginConfigurationParser();
-        File testFile = createFile("testSystemId", config);
-        try {
-            parser.validate(new StreamSource(testFile));
-            fail("plugin.xml should not validate according to schema");
-        } catch (PluginConfigurationValidatorException e) {
-            //pass
-        } finally {
-            testFile.delete();
-        }
-    }
-
-    @Test
     public void testConfigurationThatAddsNewCommand() throws UnsupportedEncodingException {
         String config = "<?xml version=\"1.0\"?>\n" +
                 "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" +
@@ -352,69 +250,6 @@
     }
 
     @Test
-    public void canValidateCorrectFile() throws IOException {
-        String config = "<?xml version=\"1.0\"?>\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" +
-                "  <commands>\n" +
-                "    <command>\n" +
-                "      <name>test</name>\n" +
-                "      <description>just a test</description>\n" +
-                "      <options>\n" +
-                "        <group>\n" +
-                "          <required>true</required>\n" +
-                "          <option>\n" +
-                "            <long>exclusive-a</long>\n" +
-                "            <short>a</short>\n" +
-                "            <argument>false</argument>\n" +
-                "            <required>false</required>\n" +
-                "            <description>exclusive option a</description>\n" +
-                "          </option>\n" +
-                "          <option>\n" +
-                "            <long>exclusive-b</long>\n" +
-                "            <short>b</short>\n" +
-                "            <argument>false</argument>\n" +
-                "            <required>false</required>\n" +
-                "            <description>exclusive option b</description>\n" +
-                "          </option>\n" +
-                "        </group>\n" +
-                "        <option>\n" +
-                "          <long>long</long>\n" +
-                "          <short>l</short>\n" +
-                "          <argument>true</argument>\n" +
-                "          <required>true</required>\n" +
-                "          <description>some required and long option</description>\n" +
-                "        </option>\n" +
-                "      </options>\n" +
-                "      <environments>" +
-                "        <environment>shell</environment>" +
-                "        <environment>cli</environment>" +
-                "      </environments>" +
-                "      <bundles>\n" +
-                "        <bundle>\n \t  \nfoo\t \n \n</bundle>\n" +
-                "        <bundle>\tbar  baz\n</bundle>\n" +
-                "        <bundle>buzz</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>";
-
-        PluginConfigurationParser parser = new PluginConfigurationParser();
-        File testFile = createFile("testSystemId", config);
-        try {
-            parser.validate(new StreamSource(testFile));
-        } catch (PluginConfigurationValidatorException e) {
-           fail("should not reach here, plugin.xml should be validated according to schema");
-        } finally {
-            testFile.delete();
-        }
-        
-    }
-    @Test
     public void testOptionParsing() throws UnsupportedEncodingException {
         String config = "<?xml version=\"1.0\"?>\n" +
                 "<plugin xmlns=\"http://icedtea.classpath.org/thermostat/plugins/v1.0\"\n" +
@@ -581,11 +416,4 @@
         assertNull(dbUrlOption);
     }
     
-    private File createFile(String fileName, String contents) throws IOException {
-        FileWriter fstream = new FileWriter(fileName);
-        BufferedWriter out = new BufferedWriter(fstream);
-        out.write(contents);
-        out.close();
-        return new File(fileName);
-    }
 }
--- a/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties	Mon Jun 03 18:10:51 2013 +0200
+++ b/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties	Wed Jun 05 11:14:27 2013 +0200
@@ -2,6 +2,7 @@
         thermostat-keyring-${project.version}.jar, \
         thermostat-storage-core-${project.version}.jar, \
         thermostat-common-core-${project.version}.jar, \
+        thermostat-plugin-validator-${project.version}.jar, \
         thermostat-launcher-${project.version}.jar, \
         thermostat-main-${project.version}.jar, \
         jline2.jar, \
--- a/pom.xml	Mon Jun 03 18:10:51 2013 +0200
+++ b/pom.xml	Wed Jun 05 11:14:27 2013 +0200
@@ -150,6 +150,7 @@
     <module>vm-jmx</module>
     <module>numa</module>
     <module>laf-utils</module>
+    <module>thermostat-plugin-validator</module>
     <module>config</module>
     <!-- development related modules -->
     <module>integration-tests</module>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/pom.xml	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat</artifactId>
+    <version>0.10.0-SNAPSHOT</version>
+  </parent>
+  
+  <artifactId>thermostat-plugin-validator</artifactId>
+  <packaging>bundle</packaging>
+  
+  <name>Thermostat Plugin Validator</name>
+  
+  <build>
+    <resources>
+      <!-- Instructions that include thermostat plugin schema into the launcher jar -->
+      <resource>
+        <directory>${basedir}/../distribution/docs/</directory>
+        <includes>
+          <include>thermostat-plugin.xsd</include>
+        </includes>
+      </resource>
+      <!-- Instructions are not cumulative to default behaviour, 
+           so it's necessary to add default behaviour instructions after explicit previous ones -->
+      <resource>
+        <directory>src/main/resources/</directory>
+        <includes>
+          <include>**/*</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>com.redhat.thermostat.plugin.validator</Bundle-SymbolicName>
+            <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
+            <Export-Package>
+              com.redhat.thermostat.plugin.validator,
+            </Export-Package>
+            <Private-Package>
+              com.redhat.thermostat.plugin.validator.internal,
+              com.redhat.thermostat.plugin.validator.locale,
+            </Private-Package>
+            <!-- Do not autogenerate uses clauses in Manifests -->
+            <_nouses>true</_nouses>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  
+  <dependencies>
+  
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-test</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+  </dependencies>
+</project>
\ 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/PluginConfigurationValidatorException.java	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+
+public class PluginConfigurationValidatorException extends Exception {
+    
+    private static final long serialVersionUID = 1L;
+    private String filePath;
+    
+    /**
+     * Constructor of PluginConfigurationValidatorException
+     * @param filePath must include the protocol 
+     * @param message the detailed message
+     */
+    public PluginConfigurationValidatorException(String filePath, String message) {
+        super(message);
+        this.filePath = computeFilePath(filePath);
+    }
+    
+    /**
+     * Constructor of PluginConfigurationValidatorException
+     * @param filePath must include the protocol 
+     * @param message the detailed message
+     */
+    public PluginConfigurationValidatorException(String filePath, String message, Throwable cause) {
+        super(message, cause);
+        this.filePath = computeFilePath(filePath);
+    }
+    
+    public String getFilePath() {
+        return filePath;
+    }
+    
+    /**
+     * Computes the file path removing the protocol scheme
+     * @param filePath must include the protocol
+     * @return the path without the protocol scheme
+     */
+    private String computeFilePath(String filePath) {
+        // the substring starts from position 5, skipping "file:" filePath content 
+        return filePath.substring(5);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/PluginValidator.java	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,71 @@
+/*
+ * 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.IOException;
+import java.net.URL;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+
+import org.xml.sax.SAXException;
+
+import com.redhat.thermostat.plugin.validator.internal.ConfigurationValidatorErrorHandler;
+
+
+public class PluginValidator {
+    
+    public void validate(StreamSource plugin) throws PluginConfigurationValidatorException {
+        URL schemaUrl = PluginValidator.class.getResource("/thermostat-plugin.xsd");
+        SchemaFactory schemaFactory = 
+                SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        
+        try {
+            Schema schema = schemaFactory.newSchema(schemaUrl);
+            Validator validator = schema.newValidator();
+            validator.setErrorHandler(new ConfigurationValidatorErrorHandler());
+            validator.validate(plugin);
+        } catch (IOException | SAXException exception) {
+            throw new PluginConfigurationValidatorException
+                (plugin.getSystemId(), exception.getLocalizedMessage(), exception);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/internal/ConfigurationValidatorErrorHandler.java	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,149 @@
+/*
+ * 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.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+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;
+
+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 enum ErrorType {
+        WARNING,
+        ERROR,
+        FATAL_ERROR;
+    }
+
+    @Override
+    public void warning(SAXParseException exception) throws SAXException {
+        warningsErrorsCounter++;
+        printInfo(exception, ErrorType.WARNING);
+    }
+
+    @Override
+    public void error(SAXParseException exception) throws SAXParseException {
+        warningsErrorsCounter++;
+        printInfo(exception, ErrorType.ERROR);
+    }
+    
+    @Override
+    public void fatalError(SAXParseException exception) throws SAXParseException {
+        if (warningsErrorsCounter == 0) {
+            printInfo(exception, ErrorType.FATAL_ERROR);
+            logger.warning("XML not well formed");
+        }
+    }
+
+    private static void printInfo(SAXParseException e, ErrorType type) {
+        int columnNumber = e.getColumnNumber();
+        int lineNumber = e.getLineNumber();
+        
+        StringBuffer buffer = new StringBuffer();
+        
+        String firstLine = null;
+        String secondLine = null;
+        String thirdLine = null;
+        String errorLine = null;
+        String pointer = "";
+        String absolutePath = e.getSystemId();
+        absolutePath = absolutePath.substring(5);
+        
+        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) {
+            System.out.println("File not found!");;
+        }
+        
+        buffer.append(translator.localize(
+                translateKeys.get(type),
+                absolutePath, 
+                Integer.toString(lineNumber), 
+                Integer.toString(columnNumber)).getContents());
+                    
+        buffer.append(formatMessage(e.getLocalizedMessage()) + "\n\n");
+        buffer.append(firstLine + "\n");
+        buffer.append(secondLine + "\n");
+        buffer.append(thirdLine + "\n");
+        buffer.append(errorLine + "\n");
+        buffer.append(pointer  + "\n");
+        
+        logger.warning("\n" + buffer.toString());
+    }
+    
+    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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/java/com/redhat/thermostat/plugin/validator/locale/LocaleResources.java	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,55 @@
+/*
+ * 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.locale;
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+
+public enum LocaleResources {
+    
+    VALIDATION_WARNING,
+    VALIDATION_ERROR,
+    VALIDATION_FATAL_ERROR,
+    ;
+
+    static final String RESOURCE_BUNDLE = "com.redhat.thermostat.plugin.validator.strings";
+
+    public static Translate<LocaleResources> createLocalizer() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/main/resources/com/redhat/thermostat/plugin/validator/strings.properties	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,3 @@
+VALIDATION_WARNING = Warning in file {0}:{1}.{2}\n
+VALIDATION_ERROR = Error in file {0}:{1}.{2}\n
+VALIDATION_FATAL_ERROR = Fatal error in file {0}:{1}.{2}\n
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/PluginValidatorTest.java	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,227 @@
+/*
+ * 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 static org.junit.Assert.fail;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import javax.xml.transform.stream.StreamSource;
+
+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(new StreamSource(testFile));
+            fail("should not come here");
+        } catch (PluginConfigurationValidatorException e) {
+            //pass
+        } finally {
+            testFile.delete();
+        }
+    }
+    
+    @Test
+    public void canValidatePluginXMLMultipleTimes() throws Exception {
+        
+        try {
+            String 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 testFile = createFile("testSystemId", config);
+            PluginValidator validator = new PluginValidator();
+            validator.validate(new StreamSource(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(new StreamSource(testFile));
+            testFile2.delete();
+        } catch (PluginConfigurationValidatorException e) {
+           fail("should not reach here, plugin.xml should be validated according to schema");
+        }
+    }
+    
+    @Test
+    public void validationFailsOnInvalidPluginFile() throws Exception {
+        String 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" +
+                "    <something>\n" +    // Error line
+                "    <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 testFile = createFile("testSystemId", config);
+        PluginValidator validator = new PluginValidator();
+        try {
+            validator.validate(new StreamSource(testFile));
+            fail("plugin.xml should not validate according to schema");
+        } catch (PluginConfigurationValidatorException e) {
+            //pass
+        } finally {
+            testFile.delete();
+        }
+    }
+    
+    @Test
+    public void canValidateCorrectFile() throws IOException {
+        String config = "<?xml version=\"1.0\"?>\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" +
+                "  <commands>\n" +
+                "    <command>\n" +
+                "      <name>test</name>\n" +
+                "      <description>just a test</description>\n" +
+                "      <options>\n" +
+                "        <group>\n" +
+                "          <required>true</required>\n" +
+                "          <option>\n" +
+                "            <long>exclusive-a</long>\n" +
+                "            <short>a</short>\n" +
+                "            <argument>false</argument>\n" +
+                "            <required>false</required>\n" +
+                "            <description>exclusive option a</description>\n" +
+                "          </option>\n" +
+                "          <option>\n" +
+                "            <long>exclusive-b</long>\n" +
+                "            <short>b</short>\n" +
+                "            <argument>false</argument>\n" +
+                "            <required>false</required>\n" +
+                "            <description>exclusive option b</description>\n" +
+                "          </option>\n" +
+                "        </group>\n" +
+                "        <option>\n" +
+                "          <long>long</long>\n" +
+                "          <short>l</short>\n" +
+                "          <argument>true</argument>\n" +
+                "          <required>true</required>\n" +
+                "          <description>some required and long option</description>\n" +
+                "        </option>\n" +
+                "      </options>\n" +
+                "      <environments>\n" +
+                "        <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" +
+                "      </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>";
+
+        File testFile = createFile("testSystemId", config);
+        PluginValidator validator = new PluginValidator();
+        try {
+            validator.validate(new StreamSource(testFile));
+        } catch (PluginConfigurationValidatorException e) {
+           fail("should not reach here, plugin.xml should be validated according to schema");
+        } finally {
+            testFile.delete();
+        }
+        
+    }
+    
+    private File createFile(String fileName, String contents) throws IOException {
+        FileWriter fstream = new FileWriter(fileName);
+        BufferedWriter out = new BufferedWriter(fstream);
+        out.write(contents);
+        out.close();
+        return new File(fileName);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin-validator/src/test/java/com/redhat/thermostat/plugin/validator/locale/LocaleResourcesTest.java	Wed Jun 05 11:14:27 2013 +0200
@@ -0,0 +1,53 @@
+/*
+ * 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.locale;
+
+import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest;
+
+public class LocaleResourcesTest extends AbstractLocaleResourcesTest<LocaleResources> {
+
+    @Override
+    protected Class<LocaleResources> getEnumClass() {
+        return LocaleResources.class;
+    }
+
+    @Override
+    protected String getResourceBundle() {
+        return LocaleResources.RESOURCE_BUNDLE;
+    }
+
+}