Fixes issue 7019, added database and module validation at the end of install.source, validates the database against the application dictionary and checks modules for filled license text/type and correct module dependencies.
authorMartin Taal <martin.taal@openbravo.com>
Thu, 22 Jan 2009 08:56:05 +0000
changeset 2686 01b9786ae86e
parent 2685 d747a87fb93d
child 2687 c808ca510e88
Fixes issue 7019, added database and module validation at the end of install.source, validates the database against the application dictionary and checks modules for filled license text/type and correct module dependencies.
Fixed dataset test so that it uses a more detailed api, made an extra method in DataSetService public.
REST: Repaired support for paging parameters in url
REST: added better error message when root tag is missing
Updated DBSourceManager to allow control over which part of the database schema is read in-memory, this to improve performance of the database validation logic.
build.xml
config/eclipse/Openbravo-eclipse-prefs.epf
src-db/database/lib/dbsourcemanager.jar
src-test/org/openbravo/test/modularity/DatasetServiceTest.java
src-test/org/openbravo/test/system/SystemValidatorTest.java
src-test/org/openbravo/test/webservice/WSReadTest.java
src-test/org/openbravo/test/webservice/WSUpdateTest.java
src/build.xml
src/org/openbravo/dal/service/OBQuery.java
src/org/openbravo/dal/xml/XMLEntityConverter.java
src/org/openbravo/service/db/DataExportService.java
src/org/openbravo/service/db/DataSetService.java
src/org/openbravo/service/rest/DalWebService.java
src/org/openbravo/service/system/ApplicationDictionaryValidator.java
src/org/openbravo/service/system/DatabaseValidator.java
src/org/openbravo/service/system/ModuleValidator.java
src/org/openbravo/service/system/SystemValidationResult.java
src/org/openbravo/service/system/SystemValidationTask.java
src/org/openbravo/service/system/SystemValidator.java
--- a/build.xml	Thu Jan 22 06:43:49 2009 +0000
+++ b/build.xml	Thu Jan 22 08:56:05 2009 +0000
@@ -235,6 +235,14 @@
 		<ant dir="${base.src}" target="compile.development" inheritAll="true" inheritRefs="true" />
 	</target>
 
+	<target name="validate.database">
+		<ant dir="${base.src}" target="validate.database" inheritAll="true" inheritRefs="true" />
+	</target>
+
+	<target name="validate.modules">
+		<ant dir="${base.src}" target="validate.modules" inheritAll="true" inheritRefs="true" />
+	</target>
+
 	<target name="compile.war" if="mode.war">
 		<antcall target="compile" />
 		<antcall target="war" />
@@ -369,12 +377,12 @@
 		<taskdef name="applyModule" classname="org.openbravo.erpCommon.modules.ApplyModuleTask">
 			<classpath refid="project.class.path" />
 		</taskdef>
-		<applyModule userId="0" propertiesFile="${base.config}/Openbravo.properties" />
-		<!-- 
-    	this antcall implements the previous method of importing data 
-    	use that if the new method does not work for you: 
-    -->
+		<applyModule userId="0" propertiesFile="${base.config}/Openbravo.properties"/>
+		
 		<antcall target="import.sample.data" />
+
+		<antcall target="validate.database" />
+		<antcall target="validate.modules" />
 	</target>
 
 	<target name="eclipse.install.source" depends="init,code.rev">
@@ -391,6 +399,9 @@
 		</taskdef>
 		<applyModule userId="0" propertiesFile="${base.config}/Openbravo.properties" />
 		<antcall target="import.sample.data" />
+
+		<antcall target="validate.database" />
+		<antcall target="validate.modules" />
 	</target>
 
 	<target name="create.database" depends="init,code.rev">
@@ -595,7 +606,14 @@
 		<taskdef name="extractModule" classname="org.openbravo.erpCommon.modules.ExtractModuleTask">
 			<classpath refid="project.class.path" />
 		</taskdef>
-
+		
+		<taskdef name="validateModules" classname="org.openbravo.service.system.SystemValidationTask">		
+			<classpath refid="project.class.path" />
+		</taskdef>
+		
+		<echo message="Validating Module..." />
+		<validateModules moduleName="${module}" failOnError="true" userId="0" propertiesFile="${base.config}/Openbravo.properties" type="module"/>
+		
 		<antcall target="obx.export.database" />
 		<antcall target="obx.export.config.script" />
 
--- a/config/eclipse/Openbravo-eclipse-prefs.epf	Thu Jan 22 06:43:49 2009 +0000
+++ b/config/eclipse/Openbravo-eclipse-prefs.epf	Thu Jan 22 08:56:05 2009 +0000
@@ -203,6 +203,7 @@
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
 /instance/org.eclipse.wst.validation/DELEGATES_PREFERENCE=delegateValidatorListorg.eclipse.wst.xsd.core.internal.validation.eclipse.XSDDelegatingValidator\=org.eclipse.wst.xsd.core.internal.validation.eclipse.Validator;org.eclipse.wst.wsdl.validation.internal.eclipse.WSDLDelegatingValidator\=org.eclipse.wst.wsdl.validation.internal.eclipse.Validator;
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+/instance/org.eclipse.core.resources/encoding=UTF-8
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.formatter.compact_else_if=true
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
 /instance/org.eclipse.jdt.core/org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
Binary file src-db/database/lib/dbsourcemanager.jar has changed
--- a/src-test/org/openbravo/test/modularity/DatasetServiceTest.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src-test/org/openbravo/test/modularity/DatasetServiceTest.java	Thu Jan 22 08:56:05 2009 +0000
@@ -19,7 +19,9 @@
 
 package org.openbravo.test.modularity;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.openbravo.base.model.Property;
 import org.openbravo.base.provider.OBProvider;
@@ -46,6 +48,10 @@
     public void testCheckQueries() {
         setErrorOccured(true);
         setUserContext("100");
+
+        Map<String, Object> parameters = new HashMap<String, Object>();
+        parameters.put("ClientID", "0");
+
         final OBCriteria<DataSet> obc = OBDal.getInstance().createCriteria(
                 DataSet.class);
         final List<DataSet> dss = obc.list();
@@ -54,7 +60,8 @@
             for (final DataSetTable dt : ds.getDataSetTableList()) {
                 try {
                     // just test but do nothing with return value
-                    DataSetService.getInstance().getExportableObjects(dt, "0");
+                    DataSetService.getInstance().getExportableObjects(dt, "0",
+                            parameters);
                 } catch (final Exception e) {
                     System.err.println(ds.getName() + ": " + dt.getEntityName()
                             + ": " + e.getMessage());
@@ -71,9 +78,13 @@
                 DataSet.class);
         final List<DataSet> dss = obc.list();
         setUserContext("0");
+
+        Map<String, Object> parameters = new HashMap<String, Object>();
+        parameters.put("ClientID", "0");
+
         for (final DataSet ds : dss) {
             final String xml = DataExportService.getInstance()
-                    .exportDataSetToXML(ds, "0");
+                    .exportDataSetToXML(ds, "0", false, parameters, true, true);
             System.err.println("DataSet " + ds.getName() + " exported "
                     + xml.length() + " characters");
         }
@@ -102,6 +113,10 @@
     public void testReadAll() {
         setErrorOccured(true);
         setBigBazaarAdminContext();
+
+        Map<String, Object> parameters = new HashMap<String, Object>();
+        parameters.put("ClientID", "0");
+
         final DataSetService dss = DataSetService.getInstance();
         final OBCriteria<DataSet> obc = OBDal.getInstance().createCriteria(
                 DataSet.class);
@@ -115,8 +130,9 @@
                 System.err.println("Exporting DataSetTable: "
                         + dt.getTable().getName());
                 final List<DataSetColumn> dcs = dss.getDataSetColumns(dt);
+
                 final List<BaseOBObject> bobs = dss.getExportableObjects(dt,
-                        "0");
+                        "0", parameters);
                 for (final BaseOBObject bob : bobs) {
                     final List<Property> ps = dss.getExportableProperties(bob,
                             dt, dcs);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src-test/org/openbravo/test/system/SystemValidatorTest.java	Thu Jan 22 08:56:05 2009 +0000
@@ -0,0 +1,76 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2008 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.test.system;
+
+import java.util.Map;
+
+import org.openbravo.service.system.ApplicationDictionaryValidator;
+import org.openbravo.service.system.ModuleValidator;
+import org.openbravo.service.system.SystemValidationResult;
+import org.openbravo.test.base.BaseTest;
+
+/**
+ * Test the System Validation.
+ * 
+ * @author mtaal
+ */
+
+public class SystemValidatorTest extends BaseTest {
+
+    public void _testSystemValidation() {
+        setErrorOccured(true);
+        setUserContext("0");
+        final ApplicationDictionaryValidator adValidator = new ApplicationDictionaryValidator();
+        final Map<String, SystemValidationResult> results = adValidator
+                .validate();
+
+        for (String key : results.keySet()) {
+            System.err
+                    .println("++++++++++++++++++++++++++++++++++++++++++++++++++");
+            System.err.println(key);
+            System.err
+                    .println("++++++++++++++++++++++++++++++++++++++++++++++++++");
+            final SystemValidationResult result = results.get(key);
+            for (String warning : result.getWarnings()) {
+                System.err.println("Warning: " + warning);
+            }
+            System.err.println("------------------------------------");
+            for (String error : result.getErrors()) {
+                System.err.println("Error: " + error);
+            }
+        }
+        setErrorOccured(false);
+    }
+
+    public void testModulesValidation() {
+        setErrorOccured(true);
+        setUserContext("0");
+        final ModuleValidator moduleValidator = new ModuleValidator();
+        final SystemValidationResult result = moduleValidator.validate();
+
+        for (String warning : result.getWarnings()) {
+            System.err.println("Warning: " + warning);
+        }
+        for (String error : result.getErrors()) {
+            System.err.println("Error: " + error);
+        }
+        setErrorOccured(false);
+    }
+}
\ No newline at end of file
--- a/src-test/org/openbravo/test/webservice/WSReadTest.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src-test/org/openbravo/test/webservice/WSReadTest.java	Thu Jan 22 08:56:05 2009 +0000
@@ -57,6 +57,21 @@
         assertTrue(index3 == -1);
     }
 
+    public void testPagedWhereClause() throws Exception {
+        String whereClause = "(table.id='104' or table.id='105') and isKey='Y'";
+        whereClause = URLEncoder.encode(whereClause, "UTF-8");
+        final String content = doTestGetRequest("/ws/dal/ADColumn?where="
+                + whereClause + "&firstResult=10&maxResult=10", "<ADColumn",
+                200);
+        // there should be two columns
+        final int index1 = content.indexOf("<ADColumn");
+        assertTrue(index1 != -1);
+        final int index2 = content.indexOf("<ADColumn", index1 + 2);
+        assertTrue(index2 != -1);
+        final int index3 = content.indexOf("<ADColumn", index2 + 2);
+        assertTrue(index3 == -1);
+    }
+
     public void testAllToXML() {
         setErrorOccured(true);
         setBigBazaarAdminContext();
--- a/src-test/org/openbravo/test/webservice/WSUpdateTest.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src-test/org/openbravo/test/webservice/WSUpdateTest.java	Thu Jan 22 08:56:05 2009 +0000
@@ -83,6 +83,16 @@
         assertTrue(content.indexOf("City id=\"" + cityId + "") != -1);
     }
 
+    public void testIncorrectRootTag() throws Exception {
+        final String city = doTestGetRequest("/ws/dal/City/" + cityId, null,
+                200);
+        System.err.println(city);
+        String newCity = city;
+        final String content = doContentRequest("/ws/dal/City/" + cityId,
+                newCity, 200, "<updated>", "POST");
+        assertTrue(content.indexOf("City id=\"" + cityId + "") != -1);
+    }
+
     public void testReadAddDeleteCity() throws Exception {
         final String city = doTestGetRequest("/ws/dal/City/" + cityId, null,
                 200);
--- a/src/build.xml	Thu Jan 22 06:43:49 2009 +0000
+++ b/src/build.xml	Thu Jan 22 08:56:05 2009 +0000
@@ -97,6 +97,20 @@
 		<mkdir dir="${build.docs}" />
 	</target>
 
+	<target name="validate.database">
+		<taskdef name="validateDatabase" classname="org.openbravo.service.system.SystemValidationTask">		
+			<classpath refid="project.class.path" />
+		</taskdef>
+		<validateDatabase userId="0" propertiesFile="${base.config}/Openbravo.properties" type="database"/>
+	</target>
+	
+	<target name="validate.modules">
+		<taskdef name="validateModules" classname="org.openbravo.service.system.SystemValidationTask">		
+			<classpath refid="project.class.path" />
+		</taskdef>
+		<validateModules userId="0" propertiesFile="${base.config}/Openbravo.properties" type="module"/>
+	</target>
+	
 	<target name="trl.clean" if="translation">
 		<java classname="org.openbravo.translate.Translate" jvm="${env.JAVA_HOME}/bin/java" fork="yes" maxmemory="${build.maxmemory}">
 			<arg line="clean ${base.config}/Openbravo.properties" />
@@ -181,8 +195,6 @@
 		</javac>
 	</target>
 
-
-
 	<target name="compileSqlc" depends="sqlc">
 		<javac srcdir="${build.sqlc}/src:${basedir}:${build.sqlc}/srcAD/org/openbravo/erpCommon/reference:${base.modules}" destdir="${build}" encoding="UTF-8" fork="true" memorymaximumsize="${build.maxmemory}" debug="true" debuglevel="lines,vars,source" deprecation="on">
 			<classpath refid="project.class.path" />
--- a/src/org/openbravo/dal/service/OBQuery.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src/org/openbravo/dal/service/OBQuery.java	Thu Jan 22 08:56:05 2009 +0000
@@ -54,6 +54,8 @@
     private boolean filterOnReadableOrganizations = true;
     private boolean filterOnReadableClients = true;
     private boolean filterOnActive = true;
+    private int firstResult = -1;
+    private int maxResult = -1;
 
     // package visible
     OBQuery() {
@@ -116,6 +118,12 @@
         try {
             final Query qry = getSession().createQuery(qryStr);
             setParameters(qry);
+            if (firstResult > -1) {
+                qry.setFirstResult(firstResult);
+            }
+            if (maxResult > -1) {
+                qry.setMaxResults(maxResult);
+            }
             return qry;
         } catch (final Exception e) {
             throw new OBException("Exception when creating query " + qryStr, e);
@@ -477,4 +485,20 @@
     public void setNamedParameters(Map<String, Object> namedParameters) {
         this.namedParameters = namedParameters;
     }
+
+    public int getFirstResult() {
+        return firstResult;
+    }
+
+    public void setFirstResult(int firstResult) {
+        this.firstResult = firstResult;
+    }
+
+    public int getMaxResult() {
+        return maxResult;
+    }
+
+    public void setMaxResult(int maxResult) {
+        this.maxResult = maxResult;
+    }
 }
\ No newline at end of file
--- a/src/org/openbravo/dal/xml/XMLEntityConverter.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src/org/openbravo/dal/xml/XMLEntityConverter.java	Thu Jan 22 08:56:05 2009 +0000
@@ -28,6 +28,7 @@
 import org.dom4j.Document;
 import org.dom4j.DocumentHelper;
 import org.dom4j.Element;
+import org.openbravo.base.exception.OBException;
 import org.openbravo.base.model.Entity;
 import org.openbravo.base.model.ModelProvider;
 import org.openbravo.base.model.Property;
@@ -160,7 +161,11 @@
 
         // check that the rootelement is the openbravo one
         final Element rootElement = doc.getRootElement();
-        Check.isSameObject(rootElement.getName(), XMLConstants.OB_ROOT_ELEMENT);
+        if (!rootElement.getName().equals(XMLConstants.OB_ROOT_ELEMENT)) {
+            throw new OBException("Root tag of the xml document should be: "
+                    + XMLConstants.OB_ROOT_ELEMENT + ", but it is "
+                    + rootElement.getName());
+        }
 
         // walk through the elements
         final Set<BaseOBObject> checkDuplicates = new HashSet<BaseOBObject>();
--- a/src/org/openbravo/service/db/DataExportService.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src/org/openbravo/service/db/DataExportService.java	Thu Jan 22 08:56:05 2009 +0000
@@ -103,10 +103,10 @@
      * 
      * @param dataSet
      *            the dataset to export
-     * @param the
-     *            moduleId is used as a parameter in where clauses of the
-     *            DataSetTable and is used to set the module id in the
-     *            AD_REF_DATA_LOADED table
+     * @param moduleId
+     *            is used as a parameter in where clauses of the DataSetTable
+     *            and is used to set the module id in the AD_REF_DATA_LOADED
+     *            table
      * @return the XML string containing the data of the dataset
      */
     public String exportDataSetToXML(DataSet dataSet, String moduleId) {
@@ -149,8 +149,28 @@
         }
     }
 
-    // note returns null if nothing has been generated
-    private String exportDataSetToXML(DataSet dataSet, String moduleId,
+    /**
+     * Export the data of a specific dataSet to XML. If the dataset is empty
+     * then a null value is returned.
+     * 
+     * @param dataSet
+     *            the dataset to export
+     * @param moduleId
+     *            is used as a parameter in where clauses of the DataSetTable
+     *            and is used to set the module id in the AD_REF_DATA_LOADED
+     *            table
+     * @param exportClientOrganizationReferences
+     *            also export the properties which reference a client or
+     *            organization
+     * @param parameters
+     *            parameters used in the wherequeries of {@link DataSetTable}
+     * @param exportTransientInfo
+     *            export transient properties
+     * @param addSystemAttributes
+     *            add Openbravo version and export time to the root tag
+     * @return the XML string containing the data of the dataset
+     */
+    public String exportDataSetToXML(DataSet dataSet, String moduleId,
             boolean exportClientOrganizationReferences,
             Map<String, Object> parameters, boolean exportTransientInfo,
             boolean addSystemAttributes) {
--- a/src/org/openbravo/service/db/DataSetService.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src/org/openbravo/service/db/DataSetService.java	Thu Jan 22 08:56:05 2009 +0000
@@ -207,10 +207,12 @@
         String whereClause = dataSetTable.getWhereClause();
 
         final Map<String, Object> existingParams = new HashMap<String, Object>();
-        if (parameters != null) {
-            for (final String name : parameters.keySet()) {
-                if (whereClause.indexOf(":" + name) != -1) {
-                    existingParams.put(name, parameters.get(name));
+        if (whereClause != null) {
+            if (parameters != null) {
+                for (final String name : parameters.keySet()) {
+                    if (whereClause.indexOf(":" + name) != -1) {
+                        existingParams.put(name, parameters.get(name));
+                    }
                 }
             }
         }
--- a/src/org/openbravo/service/rest/DalWebService.java	Thu Jan 22 06:43:49 2009 +0000
+++ b/src/org/openbravo/service/rest/DalWebService.java	Thu Jan 22 08:56:05 2009 +0000
@@ -47,6 +47,7 @@
 import org.openbravo.model.ad.datamodel.Table;
 import org.openbravo.model.ad.utility.ReferenceDataStore;
 import org.openbravo.service.web.InvalidContentException;
+import org.openbravo.service.web.InvalidRequestException;
 import org.openbravo.service.web.ResourceNotFoundException;
 import org.openbravo.service.web.WebService;
 import org.openbravo.service.web.WebServiceUtil;
@@ -119,6 +120,8 @@
                 // check if there is a whereClause
                 final String where = request.getParameter("where");
                 final String orderBy = request.getParameter("orderBy");
+                final String firstResult = request.getParameter("firstResult");
+                final String maxResult = request.getParameter("maxResult");
 
                 String whereOrderByClause = "";
                 if (where != null) {
@@ -130,6 +133,26 @@
 
                 final OBQuery<BaseOBObject> obq = OBDal.getInstance()
                         .createQuery(entityName, whereOrderByClause);
+
+                if (firstResult != null) {
+                    try {
+                        obq.setFirstResult(Integer.parseInt(firstResult));
+                    } catch (NumberFormatException e) {
+                        throw new InvalidRequestException(
+                                "Value of firstResult parameter is not an integer: "
+                                        + firstResult);
+                    }
+                }
+                if (maxResult != null) {
+                    try {
+                        obq.setMaxResult(Integer.parseInt(maxResult));
+                    } catch (NumberFormatException e) {
+                        throw new InvalidRequestException(
+                                "Value of maxResult parameter is not an integer: "
+                                        + firstResult);
+                    }
+                }
+
                 if (countOperation) {
                     response.setContentType("text/xml");
                     response.setCharacterEncoding("utf-8");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/service/system/ApplicationDictionaryValidator.java	Thu Jan 22 08:56:05 2009 +0000
@@ -0,0 +1,60 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2009 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.service.system;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This class validates several aspects of the application dictionary. The
+ * application dictionary itself is validated as well as the match between the
+ * application dictionary and the database.
+ * 
+ * @author mtaal
+ */
+public class ApplicationDictionaryValidator {
+    private static final Logger log = Logger
+            .getLogger(ApplicationDictionaryValidator.class);
+
+    private List<SystemValidator> validators = new ArrayList<SystemValidator>();
+
+    public ApplicationDictionaryValidator() {
+        validators.add(new DatabaseValidator());
+    }
+
+    /**
+     * Performs the validation using different validators. Returns the
+     * validation results grouped by type of validation.
+     * 
+     * @return the validation result.
+     */
+    public Map<String, SystemValidationResult> validate() {
+        final Map<String, SystemValidationResult> result = new HashMap<String, SystemValidationResult>();
+
+        for (SystemValidator validator : validators) {
+            result.put(validator.getCategory(), validator.validate());
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/service/system/DatabaseValidator.java	Thu Jan 22 08:56:05 2009 +0000
@@ -0,0 +1,369 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2009 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.service.system;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.ddlutils.Platform;
+import org.apache.ddlutils.PlatformFactory;
+import org.apache.ddlutils.model.Database;
+import org.apache.ddlutils.model.ForeignKey;
+import org.apache.ddlutils.model.Reference;
+import org.apache.ddlutils.model.View;
+import org.apache.log4j.Logger;
+import org.hibernate.criterion.Expression;
+import org.openbravo.base.model.Entity;
+import org.openbravo.base.model.ModelProvider;
+import org.openbravo.base.model.Property;
+import org.openbravo.base.session.OBPropertiesProvider;
+import org.openbravo.dal.service.OBCriteria;
+import org.openbravo.dal.service.OBDal;
+import org.openbravo.model.ad.datamodel.Column;
+import org.openbravo.model.ad.datamodel.Table;
+
+/**
+ * Validates the database model against the application dictionary and checks
+ * that columns are inline with the application dictionary.
+ * 
+ * @author mtaal
+ */
+public class DatabaseValidator implements SystemValidator {
+    private static final Logger log = Logger.getLogger(DatabaseValidator.class);
+
+    private Database database;
+
+    public String getCategory() {
+        return "Database Model";
+    }
+
+    public SystemValidationResult validate() {
+        initialize();
+
+        final SystemValidationResult result = new SystemValidationResult();
+
+        // read the tables
+        final OBCriteria<Table> tcs = OBDal.getInstance().createCriteria(
+                Table.class);
+        tcs.add(Expression.eq(Table.PROPERTY_ISVIEW, false));
+        final List<Table> adTables = tcs.list();
+        final Map<String, Table> adTablesByName = new HashMap<String, Table>();
+        for (Table adTable : adTables) {
+            adTablesByName.put(adTable.getTableName(), adTable);
+        }
+
+        // the following cases are checked:
+        // 1) table present in ad, but not in db
+        // 2) table present in db, not in ad
+        // 3) table present on both sides, column match check
+        final org.apache.ddlutils.model.Table[] dbTables = getDatabase()
+                .getTables();
+
+        checkTablesWithoutPrimaryKey(dbTables, result);
+
+        final Map<String, org.apache.ddlutils.model.Table> dbTablesByName = new HashMap<String, org.apache.ddlutils.model.Table>();
+        for (org.apache.ddlutils.model.Table dbTable : dbTables) {
+            dbTablesByName.put(dbTable.getName().toUpperCase(), dbTable);
+            dumpDataType(dbTable);
+
+            checkForeignKeys(dbTable, result);
+        }
+        final Map<String, org.apache.ddlutils.model.Table> tmpDBTablesByName = new HashMap<String, org.apache.ddlutils.model.Table>(
+                dbTablesByName);
+
+        final Map<String, View> dbViews = new HashMap<String, View>();
+        final View[] views = getDatabase().getViews();
+        for (View view : views) {
+            dbViews.put(view.getName().toUpperCase(), view);
+        }
+
+        for (Table adTable : adTables) {
+            final org.apache.ddlutils.model.Table dbTable = dbTablesByName
+                    .get(adTable.getTableName().toUpperCase());
+            final View view = dbViews.get(adTable.getTableName().toUpperCase());
+            if (view == null && dbTable == null) {
+                // in Application Dictionary not in Physical Table
+                result.getErrors().add(
+                        "Table " + adTable.getTableName()
+                                + " defined in the Application Dictionary"
+                                + " but is not present in the database");
+            } else if (view != null) {
+                dbViews.remove(view.getName().toUpperCase());
+            } else {
+                matchColumns(adTable, dbTable, result);
+                tmpDBTablesByName.remove(dbTable.getName().toUpperCase());
+            }
+        }
+
+        for (org.apache.ddlutils.model.Table dbTable : tmpDBTablesByName
+                .values()) {
+            result.getWarnings().add(
+                    "Table " + dbTable.getName() + " present in the database "
+                            + " but not defined in the Application Dictionary");
+        }
+
+        for (View view : dbViews.values()) {
+            result.getWarnings().add(
+                    "View " + view.getName() + " present in the database "
+                            + " but not defined in the Application Dictionary");
+        }
+        return result;
+    }
+
+    private void checkTablesWithoutPrimaryKey(
+            org.apache.ddlutils.model.Table[] dbTables,
+            SystemValidationResult result) {
+        for (org.apache.ddlutils.model.Table dbTable : dbTables) {
+            if (dbTable.getPrimaryKeyColumns().length == 0) {
+                result.getWarnings().add(
+                        "Table " + dbTable.getName()
+                                + " has no primary key columns.");
+            }
+        }
+    }
+
+    private Property getProperty(String tableName, String columnName) {
+        final Entity entity = ModelProvider.getInstance().getEntityByTableName(
+                tableName);
+        if (entity == null) {
+            // can happen with mismatches
+            return null;
+        }
+        for (Property property : entity.getProperties()) {
+            if (property.getColumnName() != null
+                    && property.getColumnName().equalsIgnoreCase(columnName)) {
+                return property;
+            }
+        }
+        return null;
+    }
+
+    private void checkForeignKeys(org.apache.ddlutils.model.Table table,
+            SystemValidationResult result) {
+        final Entity entity = ModelProvider.getInstance().getEntityByTableName(
+                table.getName());
+        if (entity == null) {
+            // can happen with mismatches
+            return;
+        }
+        for (Property property : entity.getProperties()) {
+            if (!property.isPrimitive() && !property.isOneToMany()
+                    && !property.isAuditInfo()) {
+                // check if the property column is present in a foreign key
+
+                // special case that a property does not have a column, if it is
+                // like a virtual property see ClientInformation.client
+                if (property.getColumnName() == null) {
+                    continue;
+                }
+
+                final String colName = property.getColumnName().toUpperCase();
+                boolean found = false;
+                for (ForeignKey fk : table.getForeignKeys()) {
+                    for (Reference reference : fk.getReferences()) {
+                        if (reference.getLocalColumnName().toUpperCase()
+                                .equals(colName)) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (found) {
+                        break;
+                    }
+                }
+                if (!found) {
+                    result
+                            .getWarnings()
+                            .add(
+                                    "Foreign Key Column "
+                                            + table.getName()
+                                            + "."
+                                            + property.getColumnName()
+                                            + " is not part of a foreign key constraint.");
+                }
+            }
+        }
+    }
+
+    private void matchColumns(Table adTable,
+            org.apache.ddlutils.model.Table dbTable,
+            SystemValidationResult result) {
+
+        final Map<String, org.apache.ddlutils.model.Column> dbColumnsByName = new HashMap<String, org.apache.ddlutils.model.Column>();
+        for (org.apache.ddlutils.model.Column dbColumn : dbTable.getColumns()) {
+            dbColumnsByName.put(dbColumn.getName().toUpperCase(), dbColumn);
+        }
+
+        for (Column column : adTable.getColumnList()) {
+            final org.apache.ddlutils.model.Column dbColumn = dbColumnsByName
+                    .get(column.getColumnName().toUpperCase());
+            if (dbColumn == null) {
+                result.getErrors().add(
+                        "Column " + adTable.getTableName() + "."
+                                + column.getColumnName()
+                                + " defined in the Application Dictionary "
+                                + " but not present in the database.");
+            } else {
+                checkDataType(column, dbColumn, result, dbTable);
+                dbColumnsByName.remove(column.getColumnName().toUpperCase());
+            }
+        }
+
+        for (org.apache.ddlutils.model.Column dbColumn : dbColumnsByName
+                .values()) {
+            result
+                    .getWarnings()
+                    .add(
+                            "Column "
+                                    + dbTable.getName()
+                                    + "."
+                                    + dbColumn.getName()
+                                    + " present in the database "
+                                    + " but not defined in the Application Dictionary.");
+        }
+    }
+
+    private void checkDataType(Column adColumn,
+            org.apache.ddlutils.model.Column dbColumn,
+            SystemValidationResult result,
+            org.apache.ddlutils.model.Table dbTable) {
+
+        final Property property = getProperty(dbTable.getName(), dbColumn
+                .getName());
+        if (dbColumn.isPrimaryKey()) {
+            checkType(dbColumn, dbTable, result, "VARCHAR");
+            checkLength(dbColumn, dbTable, result, 32);
+        } else if (property != null && property.getAllowedValues().size() > 0) {
+            checkType(dbColumn, dbTable, result, "NVARCHAR");
+            checkLength(dbColumn, dbTable, result, 60);
+        } else if (property != null && property.isOneToMany()) {
+            // ignore those
+        } else if (property != null && !property.isPrimitive()) {
+            checkType(dbColumn, dbTable, result, "VARCHAR");
+            checkLength(dbColumn, dbTable, result, 32);
+        } else if (property != null
+                && property.getPrimitiveObjectType() != null) {
+            final Class<?> prim = property.getPrimitiveObjectType();
+            if (prim == String.class) {
+                checkType(dbColumn, dbTable, result, new String[] { "VARCHAR",
+                        "NVARCHAR", "CHAR" });
+            } else if (prim == Integer.class) {
+                checkType(dbColumn, dbTable, result, "DECIMAL");
+            } else if (prim == BigDecimal.class) {
+                checkType(dbColumn, dbTable, result, "DECIMAL");
+            } else if (prim == Date.class) {
+                checkType(dbColumn, dbTable, result, "TIMESTAMP");
+            } else if (prim == Boolean.class) {
+                checkType(dbColumn, dbTable, result, "CHAR");
+                checkLength(dbColumn, dbTable, result, 1);
+            } else if (prim == Float.class) {
+                checkType(dbColumn, dbTable, result, "DECIMAL");
+            } else if (prim == Object.class) {
+                // nothing to check...
+            } else if (prim == Timestamp.class) {
+                checkType(dbColumn, dbTable, result, "TIMESTAMP");
+            }
+        }
+    }
+
+    private void checkType(org.apache.ddlutils.model.Column dbColumn,
+            org.apache.ddlutils.model.Table dbTable,
+            SystemValidationResult result, String[] expectedTypes) {
+        boolean found = false;
+        final StringBuilder sb = new StringBuilder();
+        for (String expectedType : expectedTypes) {
+            sb.append(expectedType + " ");
+            found = dbColumn.getType().equals(expectedType);
+            if (found) {
+                break;
+            }
+        }
+        if (!found) {
+            result.getWarnings().add(
+                    "Column " + dbTable.getName() + "." + dbColumn.getName()
+                            + " has incorrect type, expecting " + sb.toString()
+                            + "but was " + dbColumn.getType());
+        }
+    }
+
+    private void checkType(org.apache.ddlutils.model.Column dbColumn,
+            org.apache.ddlutils.model.Table dbTable,
+            SystemValidationResult result, String expectedType) {
+        if (!dbColumn.getType().equals(expectedType)) {
+            result.getWarnings().add(
+                    "Column " + dbTable.getName() + "." + dbColumn.getName()
+                            + " has incorrect type, expecting " + expectedType
+                            + " but was " + dbColumn.getType());
+        }
+    }
+
+    private void checkLength(org.apache.ddlutils.model.Column dbColumn,
+            org.apache.ddlutils.model.Table dbTable,
+            SystemValidationResult result, int expectedLength) {
+        if (dbColumn.getSizeAsInt() != expectedLength) {
+            result.getWarnings().add(
+                    "Column " + dbTable.getName() + "." + dbColumn.getName()
+                            + " has incorrect length, expecting "
+                            + expectedLength + " but was "
+                            + dbColumn.getSizeAsInt());
+        }
+    }
+
+    private void dumpDataType(org.apache.ddlutils.model.Table dbTable) {
+        // System.err.println(">>>>>>>>>>>>>> Table >>> " + dbTable.getName());
+        // for (org.apache.ddlutils.model.Column dbColumn :
+        // dbTable.getColumns()) {
+        // System.err.println(dbColumn.getType() + " " + dbColumn.getSize());
+        // }
+    }
+
+    private void initialize() {
+        final Properties props = OBPropertiesProvider.getInstance()
+                .getOpenbravoProperties();
+        final BasicDataSource ds = new BasicDataSource();
+        ds.setDriverClassName(props.getProperty("bbdd.driver"));
+        if (props.getProperty("bbdd.rdbms").equals("POSTGRE")) {
+            ds.setUrl(props.getProperty("bbdd.url") + "/"
+                    + props.getProperty("bbdd.sid"));
+        } else {
+            ds.setUrl(props.getProperty("bbdd.url"));
+        }
+        ds.setUsername(props.getProperty("bbdd.user"));
+        ds.setPassword(props.getProperty("bbdd.password"));
+        Platform platform = PlatformFactory.createNewPlatformInstance(ds);
+        platform.getModelLoader().setOnlyLoadTableColumns(true);
+        setDatabase(platform.loadModelFromDatabase(null));
+    }
+
+    public Database getDatabase() {
+        return database;
+    }
+
+    public void setDatabase(Database database) {
+        this.database = database;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/service/system/ModuleValidator.java	Thu Jan 22 08:56:05 2009 +0000
@@ -0,0 +1,209 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2009 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.service.system;
+
+import java.io.File;
+
+import org.hibernate.criterion.Expression;
+import org.openbravo.base.session.OBPropertiesProvider;
+import org.openbravo.dal.service.OBCriteria;
+import org.openbravo.dal.service.OBDal;
+import org.openbravo.model.ad.module.Module;
+import org.openbravo.model.ad.module.ModuleDependency;
+
+/**
+ * Validates modules, their dependencies and licenses
+ * 
+ * @author mtaal
+ */
+public class ModuleValidator implements SystemValidator {
+
+    public String getCategory() {
+        return "Module";
+    }
+
+    /**
+     * Validates all modules and returns the {@link SystemValidationResult}.
+     */
+    public SystemValidationResult validate() {
+        final SystemValidationResult result = new SystemValidationResult();
+        final OBCriteria<Module> modules = OBDal.getInstance().createCriteria(
+                Module.class);
+        for (Module module : modules.list()) {
+            validate(module, result);
+        }
+        return result;
+    }
+
+    public SystemValidationResult validate(String moduleName) {
+        final SystemValidationResult result = new SystemValidationResult();
+        final OBCriteria<Module> modules = OBDal.getInstance().createCriteria(
+                Module.class);
+        modules.add(Expression.eq(Module.PROPERTY_NAME, moduleName));
+
+        if (modules.list().size() == 0) {
+            result.getErrors().add("Module " + moduleName + " does not exist");
+            return result;
+        }
+        final Module module = modules.list().get(0);
+
+        validate(module, result);
+        return result;
+    }
+
+    public SystemValidationResult validateByModuleId(String moduleId) {
+        final SystemValidationResult result = new SystemValidationResult();
+
+        final Module module = OBDal.getInstance().get(Module.class, moduleId);
+
+        if (module == null) {
+            result.getErrors().add("Module " + moduleId + " does not exist");
+            return result;
+        }
+
+        validate(module, result);
+        return result;
+    }
+
+    public void validate(Module module, SystemValidationResult result) {
+        if (module.getId().equals("0")) {
+            return;
+        }
+        final String sourcePath = OBPropertiesProvider.getInstance()
+                .getOpenbravoProperties().getProperty("source.path");
+        final File modulesDir = new File(sourcePath, "modules");
+
+        final File moduleDir = new File(modulesDir, module.getJavaPackage());
+        if (!moduleDir.exists()) {
+            result.getErrors().add(
+                    "Module directory (" + moduleDir.getAbsolutePath()
+                            + ") not found, has the module been installed?");
+            return;
+        }
+
+        // check dependency on core
+        checkDepencyOnCore(module, result);
+
+        checkJavaPath(module, moduleDir, module.getJavaPackage(), result);
+
+        if (module.getLicense() == null || module.getLicenseType() == null) {
+            result.getErrors().add(
+                    "The license and/or the licenseType of the Module "
+                            + module.getName()
+                            + " are not set, before exporting these "
+                            + "fields should be set");
+        }
+
+        // industry template
+        if (module.getType().equals("T")) {
+            boolean found = false;
+            for (ModuleDependency md : module.getModuleDependencyList()) {
+                if (md.getModule().getId().equals("0") && md.isIncluded()) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                result.getErrors().add(
+                        "Module " + module.getName()
+                                + " is an Industry Template must depend "
+                                + "on Core and the dependency relation "
+                                + "must have isIncluded set to true");
+            }
+        }
+    }
+
+    private void checkJavaPath(Module module, File moduleDir,
+            String javaPackage, SystemValidationResult result) {
+        File curDir = new File(moduleDir, "src");
+        if (!curDir.exists()) {
+            return;
+        }
+        final String[] paths = javaPackage.split("\\.");
+        for (String part : paths) {
+            final File partDir = new File(curDir, part);
+            if (!partDir.exists()) {
+                result.getErrors().add(
+                        "The source directory of the Module "
+                                + module.getName()
+                                + " is invalid, it should follow the "
+                                + "javaPackage of the module: " + javaPackage);
+            }
+            if (curDir.listFiles().length > 1) {
+                result
+                        .getErrors()
+                        .add(
+                                "The source directory of the Module "
+                                        + module.getName()
+                                        + " is invalid, it contains directories "
+                                        + "which are not part of the javaPackage of the module: "
+                                        + javaPackage);
+
+            }
+            curDir = partDir;
+        }
+    }
+
+    private void checkDepencyOnCore(Module module, SystemValidationResult result) {
+        boolean coreModuleFound = false;
+        for (ModuleDependency md : module.getModuleDependencyList()) {
+            final Module dependentModule = findCoreModule(md
+                    .getDependentModule(), module.getId());
+            if (dependentModule != null) {
+                if (dependentModule.getId().equals(module.getId())) {
+                    result.getErrors().add(
+                            "Cycle in module dependencies with module "
+                                    + module.getName());
+                    coreModuleFound = true; // prevents additional message
+                    break;
+                }
+                if (dependentModule.getId().equals("0")) {
+                    coreModuleFound = true;
+                    break;
+                }
+            }
+        }
+        if (!coreModuleFound) {
+            result.getErrors().add(
+                    "Module " + module.getName() + " or any of its ancestors "
+                            + "does not depend on the Core module.");
+        }
+
+    }
+
+    // find the core module
+    private Module findCoreModule(Module module, String originalModuleId) {
+        if (module.getId().equals(originalModuleId)) {
+            return module;
+        }
+        if (module.getId().equals("0")) {
+            return module;
+        }
+
+        for (ModuleDependency md : module.getModuleDependencyList()) {
+            final Module depModule = findCoreModule(md.getDependentModule(),
+                    originalModuleId);
+            if (depModule != null) {
+                return depModule;
+            }
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/service/system/SystemValidationResult.java	Thu Jan 22 08:56:05 2009 +0000
@@ -0,0 +1,51 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2009 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.service.system;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Keeps track of the errors/warnings for a certain type of validation.
+ * 
+ * @author mtaal
+ */
+public class SystemValidationResult {
+
+    private List<String> warnings = new ArrayList<String>();
+    private List<String> errors = new ArrayList<String>();
+    private String category;
+
+    public List<String> getWarnings() {
+        return warnings;
+    }
+
+    public List<String> getErrors() {
+        return errors;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public void setCategory(String category) {
+        this.category = category;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/service/system/SystemValidationTask.java	Thu Jan 22 08:56:05 2009 +0000
@@ -0,0 +1,102 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2009 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.service.system;
+
+import org.apache.log4j.Logger;
+import org.openbravo.base.exception.OBException;
+import org.openbravo.service.db.ReferenceDataTask;
+
+/**
+ * Performs different types of validations on the basis of the type parameter.
+ * 
+ * @author mtaal
+ */
+public class SystemValidationTask extends ReferenceDataTask {
+    private static final Logger log = Logger.getLogger("SystemValidation");
+
+    private String type;
+    private boolean failOnError = false;
+    private String moduleName;
+
+    @Override
+    protected void doExecute() {
+        if (getType().contains("database")) {
+            log.info("Validating Database and Application Dictionary");
+            final DatabaseValidator databaseValidator = new DatabaseValidator();
+            printResult(databaseValidator.validate());
+        }
+        if (getType().contains("module")) {
+            log.info("Validating Modules");
+            final ModuleValidator moduleValidator = new ModuleValidator();
+            if (getModuleName() != null) {
+                printResult(moduleValidator.validate(getModuleName()));
+            } else {
+                printResult(moduleValidator.validate());
+            }
+        }
+    }
+
+    private void printResult(SystemValidationResult result) {
+        for (String warning : result.getWarnings()) {
+            log.warn("WARNING: " + warning);
+        }
+
+        if (isFailOnError()) {
+            final StringBuilder sb = new StringBuilder();
+            for (String err : result.getErrors()) {
+                sb.append(err);
+                if (sb.length() > 0) {
+                    sb.append("\n");
+                }
+            }
+            if (sb.length() > 0) {
+                throw new OBException(sb.toString());
+            }
+        } else {
+            for (String err : result.getErrors()) {
+                log.error("ERROR: " + err);
+            }
+        }
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public boolean isFailOnError() {
+        return failOnError;
+    }
+
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    public void setModuleName(String moduleName) {
+        this.moduleName = moduleName;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openbravo/service/system/SystemValidator.java	Thu Jan 22 08:56:05 2009 +0000
@@ -0,0 +1,39 @@
+/*
+ *************************************************************************
+ * The contents of this file are subject to the Openbravo  Public  License
+ * Version  1.0  (the  "License"),  being   the  Mozilla   Public  License
+ * Version 1.1  with a permitted attribution clause; you may not  use this
+ * file except in compliance with the License. You  may  obtain  a copy of
+ * the License at http://www.openbravo.com/legal/license.html 
+ * Software distributed under the License  is  distributed  on  an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific  language  governing  rights  and  limitations
+ * under the License. 
+ * The Original Code is Openbravo ERP. 
+ * The Initial Developer of the Original Code is Openbravo SL 
+ * All portions are Copyright (C) 2009 Openbravo SL 
+ * All Rights Reserved. 
+ * Contributor(s):  ______________________________________.
+ ************************************************************************
+ */
+
+package org.openbravo.service.system;
+
+/**
+ * Defines the common interface for a validator of system components like the
+ * database and the application dictionary.
+ * 
+ * @author mtaal
+ */
+public interface SystemValidator {
+
+    /**
+     * @return the validation category, mainly for reporting
+     */
+    public String getCategory();
+
+    /**
+     * @return the result of the validation, warning and error messages.
+     */
+    public SystemValidationResult validate();
+}