Changeset 3600


Ignore:
Timestamp:
Jul 25, 2007, 1:14:48 PM (16 years ago)
Author:
Nicklas Nordborg
Message:

Fixes #480: Verify values in extended-properties.xml and raw-data-types.xml

Location:
trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/doc/src/docbook/appendix/incompatible.xml

    r3518 r3600  
    7979      old behaviour use <code>ch(1) == 'NULL'</code>.
    8080    </para>
     81   
     82    <bridgehead>Extended properties and raw data types</bridgehead>
     83    <para>
     84      We have added validation code to check for invalid values. If you
     85      have modified the <filename>extended-properties.xml</filename>
     86      or the <filename>raw-data-types.xml</filename> file and they
     87      contain invalid values, you may not be able to start BASE until
     88      they are fixed. The validation is rather strict and things that may
     89      have worked before (because you were lucky or the because the database
     90      has been forgiving) may no longer work. Here is an overview of the most
     91      important validation rules:
     92    </para>
     93   
     94    <itemizedlist>
     95    <listitem>
     96      <para>
     97      Names and identifiers for extended properties and raw data type
     98      can only contain letters, digits and underscores. They must not
     99      start with a digit.
     100      </para>
     101    </listitem>
     102    <listitem>
     103      <para>
     104      Names of database tables and columns can only contain letters,
     105      digits and underscores. They must not start with a digit.
     106      </para>
     107    </listitem>
     108    <listitem>
     109      <para>
     110      There mustn't be any duplicate tables, columns, properties, etc.
     111      for a given context. For example, no duplicate tables in the
     112      database, no duplicate columns in a table, and no duplicate
     113      properties for a raw data type.
     114      </para>
     115    </listitem>
     116    </itemizedlist>
     117   
    81118  </sect1>
    82119 
  • trunk/src/core/net/sf/basedb/core/Application.java

    r3502 r3600  
    392392     
    393393      // Initialise other utility classes
     394      HibernateUtil.init1();
    394395      ExtendedProperties.init();
    395396      RawDataTypes.init();
    396397      RawDataUtil.init();
    397       HibernateUtil.init();
     398      HibernateUtil.init2();
    398399      QueryRuntimeFilterFactory.init();
    399400      PredefinedQuery.init();
  • trunk/src/core/net/sf/basedb/core/ExtendedProperties.java

    r3562 r3600  
    2424package net.sf.basedb.core;
    2525
     26import net.sf.basedb.core.dbengine.DbEngine;
     27import net.sf.basedb.util.Values;
    2628import net.sf.basedb.util.XMLUtil;
    2729
     30import java.util.HashSet;
    2831import java.util.List;
    2932import java.util.ArrayList;
    3033import java.util.Map;
    3134import java.util.HashMap;
     35import java.util.Set;
     36import java.util.regex.PatternSyntaxException;
    3237import java.net.URL;
    3338
     
    194199    List<ExtendedProperty> properties = new ArrayList<ExtendedProperty>();
    195200    List<Element> children = (List<Element>)classElement.getChildren("property");
     201    DbEngine engine = HibernateUtil.getDbEngine();
     202    String className = classElement.getAttributeValue("name");
     203    Set<String> usedNames = new HashSet<String>();
    196204    for (Element property : children)
    197205    {
    198       String name = property.getAttributeValue("name");
    199       String title = property.getAttributeValue("title");
     206      String name = Values.getStringOrNull(property.getAttributeValue("name"));
     207      if (!ExtendedProperty.isValidName(name))
     208      {
     209        throw new InvalidDataException("Invalid property for class " +
     210          className + ": name=" + name);
     211      }
     212      if (usedNames.contains("name:" + name))
     213      {
     214        throw new InvalidDataException("Duplicate property for class " +
     215          className + ": name=" + name);
     216      }
     217      usedNames.add("name:" + name);
     218      String title = Values.getStringOrNull(property.getAttributeValue("title"));
    200219      if (title == null) title = name;
     220      String column = Values.getStringOrNull(property.getAttributeValue("column"));
     221      if (!engine.isValidColumnName(column))
     222      {
     223        throw new InvalidDataException("Invalid column for property " +
     224          className + "[" + name + "]: column=" + column);
     225      }
     226      if (usedNames.contains("column:" + column))
     227      {
     228        throw new InvalidDataException("Duplicate column for property " +
     229          className + "[" + name + "]: column=" + column);
     230      }
     231      usedNames.add("column:" + column);
     232      String description = Values.getStringOrNull(property.getAttributeValue("description"));
     233      int length = XMLUtil.getIntAttribute(property, "length", 255);
    201234      Type type = Type.fromValue(property.getAttributeValue("type"));
    202       String column = property.getAttributeValue("column");
    203       String description = property.getAttributeValue("description");
    204       int length = XMLUtil.getIntAttribute(property, "length", 255);
    205235      if (type == Type.STRING && length > 255) type = Type.TEXT;
    206236      boolean nullable = XMLUtil.getBooleanAttribute(property, "null", true);
     
    230260        for (Element link : links)
    231261        {
    232           String regexp = link.getAttributeValue("regexp");
    233           String url = link.getAttributeValue("url");
    234           epLinks.add(new ExtendedPropertyLinker(regexp, url));
     262          String regexp = Values.getStringOrNull(link.getAttributeValue("regexp"));
     263          String url = Values.getStringOrNull(link.getAttributeValue("url"));
     264          if (url == null)
     265          {
     266            throw new InvalidDataException("Missing url for property link " +
     267              className + "[" + name + "]: regexp=" + regexp);
     268          }
     269          try
     270          {
     271            epLinks.add(new ExtendedPropertyLinker(regexp, url));
     272          }
     273          catch (PatternSyntaxException ex)
     274          {
     275            throw new InvalidDataException("Invalid regexp for property link " +
     276              className + "[" + name + "]: regexp=" + regexp, ex);
     277          }
    235278        }
    236279      }
  • trunk/src/core/net/sf/basedb/core/ExtendedProperty.java

    r3590 r3600  
    2626import java.text.NumberFormat;
    2727import java.util.List;
     28import java.util.regex.Pattern;
     29
    2830
    2931/**
     
    3739public class ExtendedProperty
    3840{
     41 
     42  /**
     43    A regexp checking for invalid characters.
     44  */
     45  private static final Pattern valid = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*");
     46
     47  /**
     48    Check that the name only contains a-zA-Z0-9_ and starts with
     49    a letter or underscore.
     50    @since 2.4
     51  */
     52  public static boolean isValidName(String name)
     53  {
     54    return name == null ? false : valid.matcher(name).matches();
     55  }
     56 
    3957  private final String name;
    4058  private final Type type;
  • trunk/src/core/net/sf/basedb/core/HibernateUtil.java

    r3597 r3600  
    138138
    139139  /**
    140     Initialise this class. This is done at startup time by the
    141     {@link Application#start()} method. Initialising means that we
     140    First step of initialising this class. This is done at startup time by the
     141    {@link Application#start()} method. In this step we load configuration
     142    settings from the 'base.config' and 'hibernate.cfg.xml' files and
     143    create the Dialect and DbEngine objects.
     144   
     145    Initialising means that we
    142146    load the configuration from the properties and the xml file,
    143147    read all mapping files, generate additional mappings for the
     
    145149    used by {@link Query} implementation.
    146150  */
    147   static synchronized void init()
     151  static synchronized void init1()
    148152    throws BaseException
    149153  {
     
    155159      cfg = new Configuration();
    156160      setConfigurationProperties(cfg);
     161      dialect = Dialect.getDialect(cfg.getProperties());
     162      dbEngine = EngineFactory.createEngine(dialect);
     163    }
     164    catch (HibernateException ex)
     165    {
     166      throw new BaseException(ex);
     167    }
     168  }
     169
     170  /**
     171    Second step of initialising this class. This is done at startup time by the
     172    {@link Application#start()} method. In this step we read all mapping files,
     173    generate additional mappings for the {@link ExtendableData} items and raw data,
     174    and generate filters  used by {@link Query} implementation.
     175  */
     176  static synchronized void init2()
     177    throws BaseException
     178  {
     179    // Return if we have already been initialised
     180    if (isInitialised) return;
     181 
     182    try
     183    {
    157184      addStaticMappings(cfg);
    158185      addExtendedPropertiesMappings(cfg);
     
    161188      cfg.configure();
    162189      sf = cfg.buildSessionFactory();
    163       dialect = Dialect.getDialect(cfg.getProperties());
    164       dbEngine = EngineFactory.createEngine(dialect);
    165190    }
    166191    catch (HibernateException ex)
     
    170195    isInitialised = true;
    171196  }
    172 
     197 
    173198  /**
    174199    Unload all settings.
  • trunk/src/core/net/sf/basedb/core/RawDataTypes.java

    r3562 r3600  
    2424package net.sf.basedb.core;
    2525
     26import net.sf.basedb.core.dbengine.DbEngine;
     27import net.sf.basedb.util.Values;
    2628import net.sf.basedb.util.XMLUtil;
    2729
     30import java.util.HashSet;
    2831import java.util.List;
    2932import java.util.ArrayList;
    3033import java.util.Map;
     34import java.util.Set;
    3135import java.util.TreeMap;
    3236import java.net.URL;
     
    141145  {
    142146    List<Element> rawDataTypeTags = dom.getRootElement().getChildren("raw-data-type");
     147    DbEngine engine = HibernateUtil.getDbEngine();
     148    Set<String> usedNames = new HashSet<String>();
    143149    for (Element el : rawDataTypeTags)
    144150    {
    145       String id = el.getAttributeValue("id");
    146       String name = el.getAttributeValue("name");
    147       String description = el.getAttributeValue("description");
     151      String id = Values.getStringOrNull(el.getAttributeValue("id"));
     152      String name = Values.getStringOrNull(el.getAttributeValue("name"));
     153      String description = Values.getStringOrNull(el.getAttributeValue("description"));
    148154      String storage = el.getAttributeValue("storage");
    149       String table = el.getAttributeValue("table");
     155      String table = Values.getStringOrNull(el.getAttributeValue("table"));
    150156      int channels = XMLUtil.getIntAttribute(el, "channels", 2);
    151       List<RawDataProperty> properties = loadProperties(el);
     157     
     158      if (!ExtendedProperty.isValidName(id))
     159      {
     160        throw new InvalidDataException("Invalid id for raw data type: " + id);
     161      }
     162      if (usedNames.contains("name:" + name))
     163      {
     164        throw new InvalidDataException("Duplicate name for raw data type " +
     165          id +": name=" + name);
     166      }
     167      usedNames.add("name:" + name);
     168      if ("database".equals(storage))
     169      {
     170        if (!engine.isValidTableName(table))
     171        {
     172          throw new InvalidDataException("Invalid table for raw data type " +
     173              id +": table=" + table);
     174        }
     175        if (usedNames.contains("table:" + table))
     176        {
     177          throw new InvalidDataException("Duplicate table for raw data type " +
     178            id +": table=" + table);
     179        }
     180        usedNames.add("table:" + table);
     181      }
     182      if (channels <= 0)
     183      {
     184        throw new InvalidDataException("Number of channels must be > 0 for raw data type "+
     185            id + ": channels=" + channels);
     186      }
     187      List<RawDataProperty> properties = loadProperties(el, channels);
    152188      List<IntensityFormula> formulas = loadIntensityFormulas(el, channels);
    153189      RawDataType rdt = new RawDataType(id, name, description, channels, storage, table, properties, formulas);
     
    161197  */
    162198  @SuppressWarnings({"unchecked"})
    163   private static List<RawDataProperty> loadProperties(Element rawDataTypeElement)
     199  private static List<RawDataProperty> loadProperties(Element rawDataTypeElement, int channels)
    164200  {
    165201    List<RawDataProperty> properties = new ArrayList<RawDataProperty>();
    166202    List<Element> children = rawDataTypeElement.getChildren("property");
     203    String rawDataType = rawDataTypeElement.getAttributeValue("id");
     204    DbEngine engine = HibernateUtil.getDbEngine();
     205    Set<String> usedNames = new HashSet<String>();
    167206    for (Element property : children)
    168207    {
    169       String name = property.getAttributeValue("name");
    170       String title = property.getAttributeValue("title");
     208      String name = Values.getStringOrNull(property.getAttributeValue("name"));
     209      if (!ExtendedProperty.isValidName(name))
     210      {
     211        throw new InvalidDataException("Invalid property for raw data type " +
     212          rawDataType + ": name=" + name);
     213      }
     214      if (usedNames.contains("name:" + name))
     215      {
     216        throw new InvalidDataException("Duplicate property for raw data type " +
     217          rawDataType + ": name=" + name);
     218      }
     219      usedNames.add("name:" + name);
     220      String title = Values.getStringOrNull(property.getAttributeValue("title"));
    171221      if (title == null) title = name;
    172       String column = property.getAttributeValue("column");
    173       String description = property.getAttributeValue("description");
     222      String column = Values.getStringOrNull(property.getAttributeValue("column"));
     223      if (!engine.isValidColumnName(column))
     224      {
     225        throw new InvalidDataException("Invalid column for property " +
     226          rawDataType + "[" + name + "]: column=" + column);
     227      }
     228      if (usedNames.contains("column:" + column))
     229      {
     230        throw new InvalidDataException("Duplicate column for property " +
     231          rawDataType + "[" + name + "]: column=" + column);
     232      }
     233      usedNames.add("column:" + column);
     234      String description = Values.getStringOrNull(property.getAttributeValue("description"));
    174235      Type type = Type.fromValue(property.getAttributeValue("type"));
    175236      int length = XMLUtil.getIntAttribute(property, "length", 255);
     
    192253      }
    193254      int channel = XMLUtil.getIntAttribute(property, "channel", 0);
     255      if (channel < 0 || channel > channels)
     256      {
     257        throw new InvalidDataException("Channel for property " + rawDataType+ "[" + name + "]"
     258            +  " must be >= 0 and < " + channels + ": channel=" + channel);
     259      }
    194260      properties.add(new RawDataProperty(name, title, description, column, type, length, nullable, averageMethod, channel));
    195261    }
     
    206272    List<IntensityFormula> formulas = new ArrayList<IntensityFormula>();
    207273    List<Element> children = rawDataTypeElement.getChildren("intensity-formula");
     274    String rawDataType = rawDataTypeElement.getAttributeValue("id");
     275    Set<String> usedNames = new HashSet<String>();
    208276    for (Element formula : children)
    209277    {
    210       String name = formula.getAttributeValue("name");
    211       String title = formula.getAttributeValue("title");
     278      String name = Values.getStringOrNull(formula.getAttributeValue("name"));
     279      if (!ExtendedProperty.isValidName(name))
     280      {
     281        throw new InvalidDataException("Invalid intensity formula for raw data type " +
     282          rawDataType + ": name=" + name);
     283      }
     284      if (usedNames.contains("name:" + name))
     285      {
     286        throw new InvalidDataException("Duplicate intensity formula for raw data type " +
     287          rawDataType + ": name=" + name);
     288      }
     289      usedNames.add("name:" + name);
     290      String title = Values.getStringOrNull(formula.getAttributeValue("title"));
    212291      if (title == null) title = name;
    213       String description = formula.getAttributeValue("description");
     292      String description = Values.getStringOrNull(formula.getAttributeValue("description"));
    214293      String[] expressions = new String[channels];
    215294      for (Element expression :  (List<Element>)formula.getChildren("formula"))
    216295      {
    217296        int channel = XMLUtil.getIntAttribute(expression, "channel", 0);
     297        String exp = Values.getStringOrNull(expression.getAttributeValue("expression"));
    218298        if (channel <= 0 || channel > channels)
    219299        {
    220           throw new BaseException("Invalid channel number for expression: "+expression);
     300          throw new InvalidDataException("Channel for intensity formula " +
     301              rawDataType+ "[" + name + "]"
     302              +  " must be > 0 and < " + channels + ": channel=" + channel);
    221303        }
    222304        if (expressions[channel-1] != null)
    223305        {
    224           throw new BaseException("Expression for channel "+channel+" has already been defined: " +expression);
    225         }
    226         expressions[channel-1] = expression.getAttributeValue("expression");
     306          throw new InvalidDataException("Duplicate expression for intensity formula " +
     307            rawDataType + "[" + name + "]: channel=" + channel);
     308        }
     309        if (exp == null)
     310        {
     311          throw new InvalidDataException("Missing expression for formula " +
     312            rawDataType + "[" + name + "]: channel=" + channel);
     313        }
     314        expressions[channel-1] = exp;
    227315      }
    228316      formulas.add(new IntensityFormula(name, title, description, expressions));
  • trunk/src/core/net/sf/basedb/core/data/ExtendableData.java

    r3562 r3600  
    6868    <td>
    6969      The property name of the extended property.
    70       See {@link net.sf.basedb.core.ExtendedProperty#getName()}.
     70      See {@link net.sf.basedb.core.ExtendedProperty#getName()}. The name
     71      must only contain letters, numbers and underscores but the first character
     72      can't be a number. The name must be unique within the class.
    7173    </td>
    7274  </tr>
     
    8991    <td>
    9092      The database column name of the extended property. This value
    91       must of course be unique for each class.
    92       See {@link net.sf.basedb.core.ExtendedProperty#getColumn()}.
     93      must be unique for each class. The value is validated by the
     94      {@link net.sf.basedb.core.dbengine.DbEngine#isValidColumnName(String)}
     95      which normally means it must follow the same rules as the <code>name</code>
     96      attribute. See {@link net.sf.basedb.core.ExtendedProperty#getColumn()}.
    9397    </td>
    9498  </tr>
  • trunk/src/core/net/sf/basedb/core/dbengine/AbstractDbEngine.java

    r2812 r3600  
    2424package net.sf.basedb.core.dbengine;
    2525
     26import java.util.regex.Pattern;
     27
    2628/**
    2729  An abstract superclass with default implementations for most {@link DbEngine}
     
    3638{
    3739
     40  /**
     41    A regexp checking for invalid characters.
     42  */
     43  private static final Pattern valid = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*");
     44 
    3845  /**
    3946    Create AbstractDbEngine.
     
    121128    return "EXP("+ value + ")";
    122129  }
     130  /**
     131    Checks that the name only contains the following characters: a-zA-Z0-9_
     132    It must also start with a letter or underscore.
     133    @since 2.4
     134  */
     135  public boolean isValidTableName(String tableName)
     136  {
     137    return isValidName(tableName);
     138  }
     139 
     140  /**
     141    Checks that the name only contains the following characters: a-zA-Z0-9_
     142    It must also start with a letter or underscore.
     143  */
     144  public boolean isValidColumnName(String columnName)
     145  {
     146    return isValidName(columnName);
     147  }
    123148  // -------------------------------------------
    124149
     150  /**
     151    Check that the name only contains a-zA-Z0-9_ and starts with
     152    a letter or underscore.
     153    @since 2.4
     154  */
     155  protected boolean isValidName(String name)
     156  {
     157    return name == null ? false : valid.matcher(name).matches();
     158  }
     159 
     160 
    125161}
  • trunk/src/core/net/sf/basedb/core/dbengine/DbEngine.java

    r3468 r3600  
    239239  public String exp(String value);
    240240
     241  /**
     242    Check if a given string is valid to be used as a table name in the
     243    current database.
     244    @param tableName The string to check
     245    @return TRUE if the name is valid, FALSE if not
     246    @since 2.4
     247  */
     248  public boolean isValidTableName(String tableName);
     249 
     250  /**
     251    Check if a given string is valid to be used as a column name in the
     252    current database.
     253    @param columnName The string to check
     254    @return TRUE if the name is valid, FALSE if not
     255    @since 2.4
     256  */
     257  public boolean isValidColumnName(String columnName);
    241258}
Note: See TracChangeset for help on using the changeset viewer.