File Source: DatabaseInstaller.java

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   *  contributor license agreements.  The ASF licenses this file to You
     4   * under the Apache License, Version 2.0 (the "License"); you may not
     5   * use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.  For additional information regarding
    15   * copyright in this work, please see the NOTICE file in the top level
    16   * directory of this distribution.
    17   */
    18  
    19  package org.apache.roller.weblogger.business.startup;
    20  
    21  import java.io.IOException;
    22  import java.sql.Connection;
    23  import java.sql.PreparedStatement;
    24  import java.sql.ResultSet;
    25  import java.sql.SQLException;
    26  import java.sql.Statement;
    27  import java.util.ArrayList;
    28  import java.util.Date;
    29  import java.util.List;
    30  import java.util.Properties;
    31  import org.apache.commons.logging.Log;
    32  import org.apache.commons.logging.LogFactory;
    33  import org.apache.roller.weblogger.business.DatabaseProvider;
    34  import org.apache.roller.weblogger.pojos.WeblogPermission;
    35  
    36  
    37  /**
    38   * Handles the install/upgrade of the Roller Weblogger database when the user
    39   * has configured their installation type to 'auto'.
    40   */
    41  public class DatabaseInstaller {
    42      
             /* 
    P/P       *  Method: org.apache.roller.weblogger.business.startup.DatabaseInstaller__static_init
              * 
              *  Postconditions:
              *    init'ed(log)
              */
    43      private static Log log = LogFactory.getLog(DatabaseInstaller.class);
    44      
    45      private final DatabaseProvider db;
    46      private final DatabaseScriptProvider scripts;
    47      private final String version;
    48      private List<String> messages = new ArrayList<String>();
    49      
    50      // the name of the property which holds the dbversion value
    51      private static final String DBVERSION_PROP = "roller.database.version";
    52      
    53      
             /* 
    P/P       *  Method: void org.apache.roller.weblogger.business.startup.DatabaseInstaller(DatabaseProvider, DatabaseScriptProvider)
              * 
              *  Preconditions:
              *    (soft) log != null
              * 
              *  Presumptions:
              *    java.lang.Object:getClass(...)@60 != null
              * 
              *  Postconditions:
              *    this.db == dbProvider
              *    init'ed(this.db)
              *    this.messages == &new ArrayList(DatabaseInstaller#1)
              *    this.scripts == scriptProvider
              *    init'ed(this.scripts)
              *    init'ed(this.version)
              *    new ArrayList(DatabaseInstaller#1) num objects == 1
              */
    54      public DatabaseInstaller(DatabaseProvider dbProvider, DatabaseScriptProvider scriptProvider) {
    55          db = dbProvider;
    56          scripts = scriptProvider;
    57          
    58          Properties props = new Properties();
    59          try {
    60              props.load(getClass().getResourceAsStream("/roller-version.properties"));
    61          } catch (IOException e) {
    62              log.error("roller-version.properties not found", e);
    63          }
    64          
    65          version = props.getProperty("ro.version", "UNKNOWN");
    66      }
    67      
    68      
    69      /** 
    70       * Determine if database schema needs to be upgraded.
    71       */
    72      public boolean isCreationRequired() {
                 /* 
    P/P           *  Method: bool isCreationRequired()
                  * 
                  *  Preconditions:
                  *    this.db != null
                  *    init'ed(this.db.type)
                  *    (soft) this.db.dataSource != null
                  *    (soft) init'ed(this.db.jdbcConnectionURL)
                  *    (soft) this.db.props != null
                  *    (soft) this.db.props._tainted == 0
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
    73          Connection con = null;
    74          try {            
    75              con = db.getConnection();
    76              
    77              // just check for a couple key Roller tables
+   78              if (tableExists(con, "rolleruser") && tableExists(con, "userrole")) {
    79                  return false;
    80              }
    81              
    82          } catch (Throwable t) {
    83              throw new RuntimeException("Error checking for tables", t);            
    84          } finally {
+   85              try { if (con != null) con.close(); } catch (Exception ignored) {}
    86          }
    87          
    88          return true;
    89      }
    90      
    91      
    92      /** 
    93       * Determine if database schema needs to be upgraded.
    94       */
    95      public boolean isUpgradeRequired() {
                 /* 
    P/P           *  Method: bool isUpgradeRequired()
                  * 
                  *  Preconditions:
                  *    this.version != null
                  *    (soft) log != null
                  *    (soft) this.db != null
                  *    (soft) this.db.dataSource != null
                  *    (soft) init'ed(this.db.jdbcConnectionURL)
                  *    (soft) this.db.props != null
                  *    (soft) this.db.props._tainted == 0
                  *    (soft) init'ed(this.db.type)
                  *    (soft) this.messages != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
    96          int desiredVersion = parseVersionString(version);
    97          int databaseVersion;
    98          try {
    99              databaseVersion = getDatabaseVersion();
   100          } catch (StartupException ex) {
   101              throw new RuntimeException(ex);
   102          }
   103          
   104          // if dbversion is unset then assume a new install, otherwise compare
   105          if (databaseVersion < 0) {
   106              // if this is a fresh db then we need to set the database version
   107              Connection con = null;
   108              try {
   109                  con = db.getConnection();
+  110                  setDatabaseVersion(con, version);
+  111              } catch (Exception ioe) {
   112                  errorMessage("ERROR setting database version");
   113              } finally {
   114                  try {
   115                      if (con != null) {
   116                          con.close();
   117                      }
   118                  } catch (Exception ignored) {
   119                  }
   120              }
   121  
   122              return false;
   123          } else {
   124              return databaseVersion < desiredVersion;
   125          }
   126      }
   127      
   128      
   129      public List<String> getMessages() {
                 /* 
    P/P           *  Method: List getMessages()
                  * 
                  *  Preconditions:
                  *    init'ed(this.messages)
                  * 
                  *  Postconditions:
                  *    return_value == this.messages
                  *    init'ed(return_value)
                  */
   130          return messages;
   131      }
   132      
   133      
   134      private void errorMessage(String msg) {
                 /* 
    P/P           *  Method: void errorMessage(String)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    this.messages != null
                  */
   135          messages.add(msg);
   136          log.error(msg);
   137      }    
   138      
   139      
   140      private void errorMessage(String msg, Throwable t) {
                 /* 
    P/P           *  Method: void errorMessage(String, Throwable)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    this.messages != null
                  */
   141          messages.add(msg);
   142          log.error(msg, t);
   143      }
   144      
   145      
   146      private void successMessage(String msg) {
                 /* 
    P/P           *  Method: void successMessage(String)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    this.messages != null
                  */
   147          messages.add(msg);
   148          log.trace(msg);
   149      }
   150      
   151      
   152      /**
   153       * Create datatabase tables.
   154       */
   155      public void createDatabase() throws StartupException {
   156          
                 /* 
    P/P           *  Method: void createDatabase()
                  * 
                  *  Preconditions:
                  *    log != null
                  *    org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    this.db != null
                  *    init'ed(this.db.type)
                  *    this.messages != null
                  *    this.scripts != null
                  *    this.version != null
                  *    (soft) this.db.dataSource != null
                  *    (soft) init'ed(this.db.jdbcConnectionURL)
                  *    (soft) this.db.props != null
                  *    ...
                  */
   157          log.info("Creating Roller Weblogger database tables.");
   158          
   159          Connection con = null;
   160          SQLScriptRunner create = null;
   161          try {
   162              con = db.getConnection();
+  163              String handle = getDatabaseHandle(con);
   164              create = new SQLScriptRunner(scripts.getDatabaseScript(handle + "/createdb.sql"));
   165              create.runScript(con, true);
   166              messages.addAll(create.getMessages());
   167              
   168              setDatabaseVersion(con, version);
   169              
   170          } catch (SQLException sqle) {
   171              log.error("ERROR running SQL in database creation script", sqle);
   172              if (create != null) messages.addAll(create.getMessages());
   173              errorMessage("ERROR running SQL in database creation script");
   174              throw new StartupException("Error running sql script", sqle); 
   175              
   176          } catch (Exception ioe) {
   177              log.error("ERROR running database creation script", ioe);
   178              if (create != null) messages.addAll(create.getMessages());
   179              errorMessage("ERROR reading/parsing database creation script");
   180              throw new StartupException("Error running sql script", ioe);
   181              
   182          } finally {
+  183              try { if (con != null) con.close(); } catch (Exception ignored) {}
   184          }
   185      }
   186      
   187      
   188      /**
   189       * Upgrade database if dbVersion is older than desiredVersion.
   190       */
   191      public void upgradeDatabase(boolean runScripts) throws StartupException {
   192          
                 /* 
    P/P           *  Method: void upgradeDatabase(bool)
                  * 
                  *  Preconditions:
                  *    this.version != null
                  *    (soft) log != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.db != null
                  *    (soft) this.db.dataSource != null
                  *    (soft) init'ed(this.db.jdbcConnectionURL)
                  *    (soft) this.db.props != null
                  *    (soft) this.db.props._tainted == 0
                  *    (soft) init'ed(this.db.type)
                  *    (soft) this.messages != null
                  *    ...
                  */
   193          int myVersion = parseVersionString(version);
   194          int dbversion = getDatabaseVersion();
   195          
   196          log.debug("Database version = "+dbversion);
   197          log.debug("Desired version = "+myVersion);
   198         
   199          Connection con = null;
   200          try {
   201              con = db.getConnection();
   202              if(dbversion < 0) {
   203                  String msg = "Cannot upgrade database tables, Roller database version cannot be determined";
   204                  errorMessage(msg);
+  205                  throw new StartupException(msg);
   206              } else if(dbversion >= myVersion) {
   207                  log.info("Database is current, no upgrade needed");
   208                  return;
   209              }
   210  
   211              log.info("Database is old, beginning upgrade to version "+myVersion);
   212  
   213              // iterate through each upgrade as needed
   214              // to add to the upgrade sequence simply add a new "if" statement
   215              // for whatever version needed and then define a new method upgradeXXX()
   216              if(dbversion < 130) {
+  217                  upgradeTo130(con, runScripts);
   218                  dbversion = 130;
   219              }
   220              if (dbversion < 200) {
+  221                  upgradeTo200(con, runScripts);
   222                  dbversion = 200;
   223              }
   224              if(dbversion < 210) {
+  225                  upgradeTo210(con, runScripts);
   226                  dbversion = 210;
   227              }
   228              if(dbversion < 230) {
+  229                  upgradeTo230(con, runScripts);
   230                  dbversion = 230;
   231              }
   232              if(dbversion < 240) {
+  233                  upgradeTo240(con, runScripts);
   234                  dbversion = 240;
   235              }
   236              if(dbversion < 300) {
+  237                  upgradeTo300(con, runScripts);
   238                  dbversion = 300;
   239              }
   240              if(dbversion < 310) {
+  241                  upgradeTo310(con, runScripts);
   242                  dbversion = 310;
   243              }
   244              if(dbversion < 400) {
+  245                  upgradeTo400(con, runScripts);
   246                  dbversion = 400;
   247              }
   248              if(dbversion < 401) {
+  249                  upgradeTo401(con, runScripts);
+  250                  dbversion = 401;
   251              }
   252  
   253              // make sure the database version is the exact version
   254              // we are upgrading too.
+  255              updateDatabaseVersion(con, myVersion);
   256          
   257          } catch (SQLException e) {
   258              throw new StartupException("ERROR obtaining connection");
   259          } finally {
+  260              try { if (con != null) con.close(); } catch (Exception ignored) {}
   261          }
   262      }
   263      
   264      
   265      /**
   266       * Upgrade database for Roller 1.3.0
   267       */
   268      private void upgradeTo130(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo130(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    this.messages != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.scripts != null
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:prepareStatement(...)@296 != null
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  *    java.sql.Connection:getAutoCommit(...)@302: {1}, {0}
                  */
   269          SQLScriptRunner runner = null;
   270          try {
   271              if (runScripts) {
   272                  String handle = getDatabaseHandle(con);
   273                  String scriptPath = handle + "/120-to-130-migration.sql";
   274                  successMessage("Running database upgrade script: "+scriptPath);                
   275                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   276                  runner.runScript(con, true);
   277                  messages.addAll(runner.getMessages());
   278              }
   279              
   280              /*
   281               * The new theme management code is going into place and it uses
   282               * the old website.themeEditor attribute to store a users theme.
   283               *
   284               * In pre-1.3 Roller *all* websites are considered to be using a
   285               * custom theme, so we need to make sure this is properly defined
   286               * by setting the theme on all websites to custom.
   287               *
   288               * NOTE: If we don't do this then nothing would break, but some users
   289               * would be suprised that their template customizations are no longer
   290               * in effect because they are using a shared theme instead.
   291               */
   292              
   293              successMessage("Doing upgrade to 130 ...");
   294              successMessage("Ensuring that all website themes are set to custom");
   295              
   296              PreparedStatement setCustomThemeStmt = con.prepareStatement(
   297                      "update website set editortheme = ?");
   298              
   299              setCustomThemeStmt.setString(1, org.apache.roller.weblogger.pojos.WeblogTheme.CUSTOM);
   300              setCustomThemeStmt.executeUpdate();
   301              
   302              if (!con.getAutoCommit()) con.commit();
   303              
   304              successMessage("Upgrade to 130 complete.");
   305              
   306          } catch (Exception e) {
   307              log.error("ERROR running 310 database upgrade script", e);
   308              if (runner != null) messages.addAll(runner.getMessages());
   309              
   310              errorMessage("Problem upgrading database to version 130", e);  
   311              throw new StartupException("Problem upgrading database to version 130", e);
   312          }
   313          
   314          updateDatabaseVersion(con, 130);
   315      }
   316      
   317      /**
   318       * Upgrade database for Roller 2.0.0
   319       */
   320      private void upgradeTo200(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo200(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    this.messages != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.scripts != null
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:prepareStatement(...)@335 != null
                  *    java.sql.Connection:prepareStatement(...)@338 != null
                  *    java.sql.Connection:prepareStatement(...)@340 != null
                  *    java.sql.Connection:prepareStatement(...)@344 != null
                  *    java.sql.PreparedStatement:executeQuery(...)@350 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  *    java.sql.Connection:getAutoCommit(...)@389: {1}, {0}
                  *    java.sql.ResultSet:next(...)@351: {0}, {1}
                  */
   321          SQLScriptRunner runner = null;
   322          try {
   323              if (runScripts) {
   324                  String handle = getDatabaseHandle(con);
   325                  String scriptPath = handle + "/130-to-200-migration.sql";
   326                  successMessage("Running database upgrade script: "+scriptPath);                
   327                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   328                  runner.runScript(con, true);
   329                  messages.addAll(runner.getMessages());
   330              }
   331              
   332              successMessage("Doing upgrade to 200 ...");
   333              successMessage("Populating roller_user_permissions table");
   334              
   335              PreparedStatement websitesQuery = con.prepareStatement(
   336                      "select w.id as wid, u.id as uid, u.username as uname from "
   337                      + "website as w, rolleruser as u where u.id=w.userid");
   338              PreparedStatement websiteUpdate = con.prepareStatement(
   339                      "update website set handle=? where id=?");
   340              PreparedStatement entryUpdate = con.prepareStatement(
   341                      "update weblogentry set userid=?, status=?, "
   342                      + "pubtime=pubtime, updatetime=updatetime "
   343                      + "where publishentry=? and websiteid=?");
   344              PreparedStatement permsInsert = con.prepareStatement(
   345                      "insert into roller_user_permissions "
   346                      + "(id, website_id, user_id, permission_mask, pending) "
   347                      + "values (?,?,?,?,?)");
   348              
   349              // loop through websites, each has a user
   350              ResultSet websiteSet = websitesQuery.executeQuery();
   351              while (websiteSet.next()) {
   352                  String websiteid = websiteSet.getString("wid");
   353                  String userid = websiteSet.getString("uid");
   354                  String handle = websiteSet.getString("uname");
   355                  successMessage("Processing website: " + handle);
   356                  
   357                  // use website user's username as website handle
   358                  websiteUpdate.clearParameters();
   359                  websiteUpdate.setString(1, handle);
   360                  websiteUpdate.setString(2, websiteid);
   361                  websiteUpdate.executeUpdate();
   362                  
   363                  // update all of pubished entries to include userid and status
   364                  entryUpdate.clearParameters();
   365                  entryUpdate.setString( 1, userid);
   366                  entryUpdate.setString( 2, "PUBLISHED");
   367                  entryUpdate.setBoolean(3, true);
   368                  entryUpdate.setString( 4, websiteid);
   369                  entryUpdate.executeUpdate();
   370                  
   371                  // update all of draft entries to include userid and status
   372                  entryUpdate.clearParameters();
   373                  entryUpdate.setString( 1, userid);
   374                  entryUpdate.setString( 2, "DRAFT");
   375                  entryUpdate.setBoolean(3, false);
   376                  entryUpdate.setString( 4, websiteid);
   377                  entryUpdate.executeUpdate();
   378                  
   379                  // add  permission for user in website
   380                  permsInsert.clearParameters();
   381                  permsInsert.setString( 1, websiteid+"p");
   382                  permsInsert.setString( 2, websiteid);
   383                  permsInsert.setString( 3, userid);
   384                  permsInsert.setShort(    4,WeblogPermission.ADMIN);
   385                  permsInsert.setBoolean(5, false);
   386                  permsInsert.executeUpdate();
   387              }
   388              
   389              if (!con.getAutoCommit()) con.commit();
   390              
   391              successMessage("Upgrade to 200 complete.");
   392              
   393          } catch (Exception e) {
   394              log.error("ERROR running 310 database upgrade script", e);
   395              if (runner != null) messages.addAll(runner.getMessages());
   396              
   397              errorMessage("Problem upgrading database to version 200", e);
   398              throw new StartupException("Problem upgrading database to version 200", e);
   399          }
   400          
   401          updateDatabaseVersion(con, 200);
   402      }
   403      
   404      
   405      /**
   406       * Upgrade database for Roller 2.1.0
   407       */
   408      private void upgradeTo210(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo210(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    this.messages != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.scripts != null
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:prepareStatement(...)@434 != null
                  *    java.sql.Connection:prepareStatement(...)@439 != null
                  *    java.sql.Connection:prepareStatement(...)@442 != null
                  *    java.sql.Connection:prepareStatement(...)@446 != null
                  *    java.sql.Connection:prepareStatement(...)@452 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  *    java.sql.Connection:getAutoCommit(...)@508: {1}, {0}
                  *    java.sql.ResultSet:next(...)@460: {0}, {1}
                  *    java.sql.ResultSet:next(...)@473: {0}, {1}
                  */
   409          SQLScriptRunner runner = null;
   410          try {
   411              if (runScripts) {
   412                  String handle = getDatabaseHandle(con);
   413                  String scriptPath = handle + "/200-to-210-migration.sql";
   414                  successMessage("Running database upgrade script: "+scriptPath);                
   415                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   416                  runner.runScript(con, true);
   417                  messages.addAll(runner.getMessages());
   418              }
   419              
   420              /*
   421               * For Roller 2.1.0 we are going to standardize some of the
   422               * weblog templates and make them less editable.  To do this
   423               * we need to do a little surgery.
   424               *
   425               * The goal for this upgrade is to ensure that ALL weblogs now have
   426               * the required "Weblog" template as their default template.
   427               */
   428              
   429              successMessage("Doing upgrade to 210 ...");
   430              successMessage("Ensuring that all weblogs use the 'Weblog' template as their default page");
   431              
   432              // this query will give us all websites that have modified their
   433              // default page to link to something other than "Weblog"
   434              PreparedStatement selectUpdateWeblogs = con.prepareStatement(
   435                      "select website.id,template,website.handle from website,webpage "+
   436                      "where webpage.id = website.defaultpageid "+
   437                      "and webpage.link != 'Weblog'");
   438              
   439              PreparedStatement selectWeblogTemplate = con.prepareStatement(
   440                      "select id from webpage where websiteid = ? and link = 'Weblog'");
   441              
   442              PreparedStatement updateWeblogTemplate = con.prepareStatement(
   443                      "update webpage set template = ? where id = ?");
   444              
   445              // insert a new template for a website
   446              PreparedStatement insertWeblogTemplate = con.prepareStatement(
   447                      "insert into webpage"+
   448                      "(id, name, description, link, websiteid, template, updatetime) "+
   449                      "values(?,?,?,?,?,?,?)");
   450              
   451              // update the default page for a website
   452              PreparedStatement updateDefaultPage = con.prepareStatement(
   453                      "update website set defaultpageid = ? "+
   454                      "where id = ?");
   455              
   456              String description = "This template is used to render the main "+
   457                      "page of your weblog.";
   458              ResultSet websiteSet = selectUpdateWeblogs.executeQuery();
   459              Date now = new Date();
   460              while (websiteSet.next()) {
   461                  String websiteid = websiteSet.getString(1);
   462                  String template = websiteSet.getString(2);
   463                  String handle = websiteSet.getString(3);
   464                  successMessage("Processing website: " + handle);
   465                  
   466                  String defaultpageid = null;
   467                  
   468                  // it's possible that this weblog has a "Weblog" template, but just
   469                  // isn't using it as their default.  if so we need to fix that.
   470                  selectWeblogTemplate.clearParameters();
   471                  selectWeblogTemplate.setString(1, websiteid);
   472                  ResultSet weblogPageSet = selectWeblogTemplate.executeQuery();
   473                  if(weblogPageSet.next()) {
   474                      // this person already has a "Weblog" template, so update it
   475                      String id = weblogPageSet.getString(1);
   476                      
   477                      updateWeblogTemplate.clearParameters();
   478                      updateWeblogTemplate.setString(1, template);
   479                      updateWeblogTemplate.setString(2, id);
   480                      updateWeblogTemplate.executeUpdate();
   481                      
   482                      // make sure and adjust what default page id we want to use
   483                      defaultpageid = id;
   484                  } else {
   485                      // no "Weblog" template, so insert a new one
   486                      insertWeblogTemplate.clearParameters();
   487                      insertWeblogTemplate.setString( 1, websiteid+"q");
   488                      insertWeblogTemplate.setString( 2, "Weblog");
   489                      insertWeblogTemplate.setString( 3, description);
   490                      insertWeblogTemplate.setString( 4, "Weblog");
   491                      insertWeblogTemplate.setString( 5, websiteid);
   492                      insertWeblogTemplate.setString( 6, template);
   493                      insertWeblogTemplate.setDate(   7, new java.sql.Date(now.getTime()));
   494                      insertWeblogTemplate.executeUpdate();
   495                      
   496                      // set the new default page id
   497                      defaultpageid = websiteid+"q";
   498                  }
   499                  
   500                  // update defaultpageid value
   501                  updateDefaultPage.clearParameters();
   502                  updateDefaultPage.setString( 1, defaultpageid);
   503                  updateDefaultPage.setString( 2, websiteid);
   504                  updateDefaultPage.executeUpdate();
   505              }
   506              
   507              
   508              if (!con.getAutoCommit()) con.commit();
   509              
   510              successMessage("Upgrade to 210 complete.");
   511              
   512          } catch (Exception e) {
   513              log.error("ERROR running 310 database upgrade script", e);
   514              if (runner != null) messages.addAll(runner.getMessages());
   515              
   516              log.error("Problem upgrading database to version 210", e);
   517              throw new StartupException("Problem upgrading database to version 210", e);
   518          }
   519          
   520          updateDatabaseVersion(con, 210);
   521      }
   522      
   523      
   524      /**
   525       * Upgrade database for Roller 2.3.0
   526       */
   527      private void upgradeTo230(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo230(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.messages != null
                  *    (soft) this.scripts != null
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  */
   528          SQLScriptRunner runner = null;
   529          try {
   530              if (runScripts) {
   531                  String handle = getDatabaseHandle(con);
   532                  String scriptPath = handle + "/210-to-230-migration.sql";
   533                  successMessage("Running database upgrade script: "+scriptPath);                
   534                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   535                  runner.runScript(con, true);
   536                  messages.addAll(runner.getMessages());
   537              }
   538          } catch (Exception e) {
   539              log.error("ERROR running 310 database upgrade script", e);
   540              if (runner != null) messages.addAll(runner.getMessages());
   541              
   542              errorMessage("Problem upgrading database to version 230", e);
   543              throw new StartupException("Problem upgrading database to version 230", e);
   544          }
   545          
   546          updateDatabaseVersion(con, 230);
   547      }
   548      
   549      
   550      /**
   551       * Upgrade database for Roller 2.4.0
   552       */
   553      private void upgradeTo240(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo240(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.messages != null
                  *    (soft) this.scripts != null
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  */
   554          SQLScriptRunner runner = null;
   555          try {
   556              if (runScripts) {
   557                  String handle = getDatabaseHandle(con);
   558                  String scriptPath = handle + "/230-to-240-migration.sql";
   559                  successMessage("Running database upgrade script: "+scriptPath);                
   560                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   561                  runner.runScript(con, true);
   562                  messages.addAll(runner.getMessages());
   563              }
   564          } catch (Exception e) {
   565              log.error("ERROR running 310 database upgrade script", e);
   566              if (runner != null) messages.addAll(runner.getMessages());
   567              
   568              errorMessage("Problem upgrading database to version 240", e);
   569              throw new StartupException("Problem upgrading database to version 240", e);
   570          }
   571          
   572          updateDatabaseVersion(con, 240);
   573      }
   574      
   575      
   576      /**
   577       * Upgrade database for Roller 3.0.0
   578       */
   579      private void upgradeTo300(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo300(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    this.messages != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.scripts != null
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:prepareStatement(...)@608 != null
                  *    java.sql.Connection:prepareStatement(...)@611 != null
                  *    java.sql.Connection:prepareStatement(...)@622 != null
                  *    java.sql.Connection:prepareStatement(...)@628 != null
                  *    java.sql.PreparedStatement:executeQuery(...)@631 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  *    java.sql.Connection:getAutoCommit(...)@644: {1}, {0}
                  *    java.sql.ResultSet:next(...)@632: {0}, {1}
                  */
   580          SQLScriptRunner runner = null;
   581          try {
   582              if (runScripts) {
   583                  String handle = getDatabaseHandle(con);
   584                  String scriptPath = handle + "/240-to-300-migration.sql";
   585                  successMessage("Running database upgrade script: "+scriptPath);                
   586                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   587                  runner.runScript(con, true);
   588                  messages.addAll(runner.getMessages());
   589              }
   590              
   591              /*
   592               * For Roller 3.0.0 we are allowing each weblogentry to track a
   593               * locale now so that we can support multi-lingual blogs.  As part
   594               * of the upgrade process we want to do 2 things ..
   595               *
   596               * 1. make sure all weblogs have a locale
   597               * 2. set the locale on all entries to the locale for the weblog
   598               */
   599              
   600              successMessage("Doing upgrade to 300 ...");
   601              
   602              // get system default language
   603              String locale = java.util.Locale.getDefault().getLanguage();
   604              
   605              successMessage("Setting website locale to "+locale+" for websites with no locale");
   606              
   607              // update all weblogs where locale is "null"
   608              PreparedStatement updateNullWeblogLocale = con.prepareStatement(
   609                      "update website set locale = ? where locale is NULL");
   610              // update all weblogs where locale is empty string ""
   611              PreparedStatement updateEmptyWeblogLocale = con.prepareStatement(
   612                      "update website set locale = ? where locale = ''");
   613              updateNullWeblogLocale.setString( 1, locale);
   614              updateEmptyWeblogLocale.setString( 1, locale);
   615              updateNullWeblogLocale.executeUpdate();
   616              updateEmptyWeblogLocale.executeUpdate();
   617  
   618              
   619              successMessage("Setting weblogentry locales to website locale");
   620              
   621              // get all entries and the locale of its website
   622              PreparedStatement selectWeblogsLocale = con.prepareStatement(
   623                      "select weblogentry.id,website.locale "+
   624                      "from weblogentry,website "+
   625                      "where weblogentry.websiteid = website.id");
   626              
   627              // set the locale for an entry
   628              PreparedStatement updateWeblogLocale = con.prepareStatement(
   629                      "update weblogentry set locale = ? where id = ?");
   630              
   631              ResultSet websiteSet = selectWeblogsLocale.executeQuery();
   632              while (websiteSet.next()) {
   633                  String entryid = websiteSet.getString(1);
   634                  String entrylocale = websiteSet.getString(2);
   635                  
   636                  // update entry locale
   637                  updateWeblogLocale.clearParameters();
   638                  updateWeblogLocale.setString( 1, entrylocale);
   639                  updateWeblogLocale.setString( 2, entryid);
   640                  updateWeblogLocale.executeUpdate();
   641              }
   642              
   643              
   644              if (!con.getAutoCommit()) con.commit();
   645              
   646              successMessage("Upgrade to 300 complete.");
   647              
   648          } catch (Exception e) {
   649              log.error("ERROR running 310 database upgrade script", e);
   650              if (runner != null) messages.addAll(runner.getMessages());
   651              
   652              errorMessage("Problem upgrading database to version 300", e);
   653              throw new StartupException("Problem upgrading database to version 300", e);
   654          }
   655          
   656          updateDatabaseVersion(con, 300);
   657      }
   658      
   659      
   660      /**
   661       * Upgrade database for Roller 3.1.0
   662       */
   663      private void upgradeTo310(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo310(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.messages != null
                  *    (soft) this.scripts != null
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  */
   664          SQLScriptRunner runner = null;
   665          try {
   666              if (runScripts) {
   667                  String handle = getDatabaseHandle(con);
   668                  String scriptPath = handle + "/300-to-310-migration.sql";
   669                  successMessage("Running database upgrade script: "+scriptPath);                
   670                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   671                  runner.runScript(con, true);
   672                  messages.addAll(runner.getMessages());
   673              }
   674          } catch (Exception e) {
   675              log.error("ERROR running 310 database upgrade script", e);
   676              if (runner != null) messages.addAll(runner.getMessages());
   677              
   678              errorMessage("Problem upgrading database to version 310", e);
   679              throw new StartupException("Problem upgrading database to version 310", e);
   680          }
   681          
   682          updateDatabaseVersion(con, 310);
   683      }
   684      
   685      
   686      /**
   687       * Upgrade database for Roller 4.0.0
   688       */
   689      private void upgradeTo400(Connection con, boolean runScripts) throws StartupException {
   690          
                 /* 
    P/P           *  Method: void upgradeTo400(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    this.messages != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.scripts != null
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:prepareStatement(...)@1030 != null
                  *    java.sql.Connection:prepareStatement(...)@1043 != null
                  *    java.sql.Connection:prepareStatement(...)@1055 != null
                  *    java.sql.Connection:prepareStatement(...)@1082 != null
                  *    java.sql.Connection:prepareStatement(...)@1090 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  *    java.lang.String:equals(...)@1035: {0}, {1}
                  *    java.lang.String:equals(...)@1049: {1}, {0}
                  *    java.lang.String:length(...)@989: {0}, {1..232-1}
                  *    java.lang.String:startsWith(...)@1003: {0}, {1}
                  *    java.sql.Connection:getAutoCommit(...)@1013: {1}, {0}
                  *    java.sql.Connection:getAutoCommit(...)@1103: {1}, {0}
                  *    java.sql.Connection:getAutoCommit(...)@762: {1}, {0}
                  *    java.sql.Connection:getAutoCommit(...)@911: {1}, {0}
                  *    java.sql.Connection:getAutoCommit(...)@966: {1}, {0}
                  *    ...
                  */
   691          successMessage("Doing upgrade to 400 ...");
   692          
   693          // first we need to run upgrade scripts 
   694          SQLScriptRunner runner = null;
   695          try {    
   696              if (runScripts) {
   697                  String handle = getDatabaseHandle(con);
   698                  String scriptPath = handle + "/310-to-400-migration.sql";
   699                  successMessage("Running database upgrade script: "+scriptPath);                
   700                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
   701                  runner.runScript(con, true);
   702                  messages.addAll(runner.getMessages());
   703              }
   704          } catch(Exception ex) {
   705              log.error("ERROR running 400 database upgrade script", ex);
   706              if (runner != null) messages.addAll(runner.getMessages());
   707              
   708              errorMessage("Problem upgrading database to version 400", ex);
   709              throw new StartupException("Problem upgrading database to version 400", ex);
   710          }
   711          
   712          
   713          // now upgrade hierarchical objects data model
   714          try {
   715              successMessage("Populating parentid columns for weblogcategory and folder tables");
   716              
   717              // Populate parentid in weblogcategory and folder tables.
   718              //
   719              // We'd like to do something like the below, but few databases 
   720              // support multiple table udpates, which are part of SQL-99
   721              //
   722              // update weblogcategory, weblogcategoryassoc 
   723              //   set weblogcategory.parentid = weblogcategoryassoc.ancestorid 
   724              //   where 
   725              //      weblogcategory.id = weblogcategoryassoc.categoryid 
   726              //      and weblogcategoryassoc.relation = 'PARENT';
   727              //
   728              // update folder,folderassoc 
   729              //   set folder.parentid = folderassoc.ancestorid 
   730              //   where 
   731              //      folder.id = folderassoc.folderid 
   732              //      and folderassoc.relation = 'PARENT';
   733              
   734              PreparedStatement selectParents = con.prepareStatement(
   735                  "select categoryid, ancestorid from weblogcategoryassoc where relation='PARENT'");
   736              PreparedStatement updateParent = con.prepareStatement(
   737                  "update weblogcategory set parentid=? where id=?");            
   738              ResultSet parentSet = selectParents.executeQuery();
   739              while (parentSet.next()) {
   740                  String categoryid = parentSet.getString(1);
   741                  String parentid = parentSet.getString(2);                
   742                  updateParent.clearParameters();
   743                  updateParent.setString( 1, parentid);
   744                  updateParent.setString( 2, categoryid);
   745                  updateParent.executeUpdate();
   746              }
   747              
   748              selectParents = con.prepareStatement(
   749                  "select folderid, ancestorid from folderassoc where relation='PARENT'");
   750              updateParent = con.prepareStatement(
   751                  "update folder set parentid=? where id=?");            
   752              parentSet = selectParents.executeQuery();
   753              while (parentSet.next()) {
   754                  String folderid = parentSet.getString(1);
   755                  String parentid = parentSet.getString(2);                
   756                  updateParent.clearParameters();
   757                  updateParent.setString( 1, parentid);
   758                  updateParent.setString( 2, folderid);
   759                  updateParent.executeUpdate();
   760              }
   761              
   762              if (!con.getAutoCommit()) con.commit();
   763             
   764              successMessage("Done populating parentid columns.");
   765              
   766          } catch (Exception e) {
   767              errorMessage("Problem upgrading database to version 320", e);
   768              throw new StartupException("Problem upgrading database to version 320", e);
   769          }
   770          
   771          
   772          try {
   773              successMessage("Populating path columns for weblogcategory and folder tables.");
   774                          
   775              // Populate path in weblogcategory and folder tables.
   776              //
   777              // It would be nice if there was a simple sql solution for doing
   778              // this, but sadly the only real way to do it is through brute
   779              // force walking the hierarchical trees.  Luckily, it seems that
   780              // most people don't create multi-level hierarchies, so hopefully
   781              // this won't be too bad
   782              
   783              // set path to '/' for nodes with no parents (aka root nodes)
   784              PreparedStatement setRootPaths = con.prepareStatement(
   785                  "update weblogcategory set path = '/' where parentid is NULL");
   786              setRootPaths.clearParameters();
   787              setRootPaths.executeUpdate();
   788              
   789              // select all nodes whose parent has no parent (aka 1st level nodes)
   790              PreparedStatement selectL1Children = con.prepareStatement(
   791                  "select f.id, f.name from weblogcategory f, weblogcategory p "+
   792                      "where f.parentid = p.id and p.parentid is NULL");
   793              // update L1 nodes with their path (/<name>)
   794              PreparedStatement updateL1Children = con.prepareStatement(
   795                  "update weblogcategory set path=? where id=?");
   796              ResultSet L1Set = selectL1Children.executeQuery();
   797              while (L1Set.next()) {
   798                  String id = L1Set.getString(1);
   799                  String name = L1Set.getString(2);                
   800                  updateL1Children.clearParameters();
   801                  updateL1Children.setString( 1, "/"+name);
   802                  updateL1Children.setString( 2, id);
   803                  updateL1Children.executeUpdate();
   804              }
   805              
   806              // now for the complicated part =(
   807              // we need to keep iterating over L2, L3, etc nodes and setting
   808              // their path until all nodes have been updated.
   809              
   810              // select all nodes whose parent path has been set, excluding L1 nodes
   811              PreparedStatement selectLxChildren = con.prepareStatement(
   812                  "select f.id, f.name, p.path from weblogcategory f, weblogcategory p "+
   813                      "where f.parentid = p.id and p.path <> '/' "+
   814                      "and p.path is not NULL and f.path is NULL");
   815              // update Lx nodes with their path (<parentPath>/<name>)
   816              PreparedStatement updateLxChildren = con.prepareStatement(
   817                  "update weblogcategory set path=? where id=?");
   818              
   819              // this loop allows us to run this part of the upgrade process as
   820              // long as is necessary based on the depth of the hierarchy, and
   821              // we use the do/while construct to ensure it's run at least once
   822              int catNumCounted = 0;
   823              do {
   824                  log.debug("Doing pass over Lx children for categories");
   825                  
   826                  // reset count for each iteration of outer loop
   827                  catNumCounted = 0;
   828                  
   829                  ResultSet LxSet = selectLxChildren.executeQuery();
   830                  while (LxSet.next()) {
   831                      String id = LxSet.getString(1);
   832                      String name = LxSet.getString(2);
   833                      String parentPath = LxSet.getString(3);
   834                      updateLxChildren.clearParameters();
   835                      updateLxChildren.setString( 1, parentPath+"/"+name);
   836                      updateLxChildren.setString( 2, id);
   837                      updateLxChildren.executeUpdate();
   838                      
   839                      // count the updated rows
+  840                      catNumCounted++;
   841                  }
   842                  
   843                  log.debug("Updated "+catNumCounted+" Lx category paths");
   844              } while(catNumCounted > 0);
   845              
   846              
   847              
   848              // set path to '/' for nodes with no parents (aka root nodes)
   849              setRootPaths = con.prepareStatement(
   850                  "update folder set path = '/' where parentid is NULL");
   851              setRootPaths.clearParameters();
   852              setRootPaths.executeUpdate();
   853              
   854              // select all nodes whose parent has no parent (aka 1st level nodes)
   855              selectL1Children = con.prepareStatement(
   856                  "select f.id, f.name from folder f, folder p "+
   857                      "where f.parentid = p.id and p.parentid is NULL");
   858              // update L1 nodes with their path (/<name>)
   859              updateL1Children = con.prepareStatement(
   860                  "update folder set path=? where id=?");
   861              L1Set = selectL1Children.executeQuery();
   862              while (L1Set.next()) {
   863                  String id = L1Set.getString(1);
   864                  String name = L1Set.getString(2);                
   865                  updateL1Children.clearParameters();
   866                  updateL1Children.setString( 1, "/"+name);
   867                  updateL1Children.setString( 2, id);
   868                  updateL1Children.executeUpdate();
   869              }
   870              
   871              // now for the complicated part =(
   872              // we need to keep iterating over L2, L3, etc nodes and setting
   873              // their path until all nodes have been updated.
   874              
   875              // select all nodes whose parent path has been set, excluding L1 nodes
   876              selectLxChildren = con.prepareStatement(
   877                  "select f.id, f.name, p.path from folder f, folder p "+
   878                      "where f.parentid = p.id and p.path <> '/' "+
   879                      "and p.path is not NULL and f.path is NULL");
   880              // update Lx nodes with their path (/<name>)
   881              updateLxChildren = con.prepareStatement(
   882                  "update folder set path=? where id=?");
   883              
   884              // this loop allows us to run this part of the upgrade process as
   885              // long as is necessary based on the depth of the hierarchy, and
   886              // we use the do/while construct to ensure it's run at least once
   887              int folderNumUpdated = 0;
   888              do {
   889                  log.debug("Doing pass over Lx children for folders");
   890                  
   891                  // reset count for each iteration of outer loop
   892                  folderNumUpdated = 0;
   893                  
   894                  ResultSet LxSet = selectLxChildren.executeQuery();
   895                  while (LxSet.next()) {
   896                      String id = LxSet.getString(1);
   897                      String name = LxSet.getString(2);
   898                      String parentPath = LxSet.getString(3);
   899                      updateLxChildren.clearParameters();
   900                      updateLxChildren.setString( 1, parentPath+"/"+name);
   901                      updateLxChildren.setString( 2, id);
   902                      updateLxChildren.executeUpdate();
   903                      
   904                      // count the updated rows
+  905                      folderNumUpdated++;
   906                  }
   907                  
   908                  log.debug("Updated "+folderNumUpdated+" Lx folder paths");
   909              } while(folderNumUpdated > 0);
   910              
   911              if (!con.getAutoCommit()) con.commit();
   912             
   913              successMessage("Done populating path columns.");
   914              
   915          } catch (SQLException e) {
   916              log.error("Problem upgrading database to version 320", e);
   917              throw new StartupException("Problem upgrading database to version 320", e);
   918          }
   919          
   920          
   921          // 4.0 changes the planet data model a bit, so we need to clean that up
   922          try {
   923              successMessage("Merging planet groups 'all' and 'external'");
   924              
   925              // Move all subscriptions in the planet group 'external' to group 'all'
   926              
   927              String allGroupId = null;
   928              PreparedStatement selectAllGroupId = con.prepareStatement(
   929                  "select id from rag_group where handle = 'all'");
   930              ResultSet rs = selectAllGroupId.executeQuery();
   931              if (rs.next()) {
   932                  allGroupId = rs.getString(1);
   933              }
   934              
   935              String externalGroupId = null;
   936              PreparedStatement selectExternalGroupId = con.prepareStatement(
   937                  "select id from rag_group where handle = 'external'");            
   938              rs = selectExternalGroupId.executeQuery();
   939              if (rs.next()) {
   940                  externalGroupId = rs.getString(1);
   941              }
   942              
   943              // we only need to merge if both of those groups already existed
   944              if(allGroupId != null && externalGroupId != null) {
   945                  PreparedStatement updateGroupSubs = con.prepareStatement(
   946                          "update rag_group_subscription set group_id = ? where group_id = ?");
   947                  updateGroupSubs.clearParameters();
   948                  updateGroupSubs.setString( 1, allGroupId);
   949                  updateGroupSubs.setString( 2, externalGroupId);
   950                  updateGroupSubs.executeUpdate();
   951                  
   952                  // we no longer need the group 'external'
   953                  PreparedStatement deleteExternalGroup = con.prepareStatement(
   954                          "delete from rag_group where handle = 'external'");
   955                  deleteExternalGroup.executeUpdate();
   956                  
   957              // if we only have group 'external' then just rename it to 'all'
   958              } else if(allGroupId == null && externalGroupId != null) {
   959                  
   960                  // rename 'external' to 'all'
   961                  PreparedStatement renameExternalGroup = con.prepareStatement(
   962                          "update rag_group set handle = 'all' where handle = 'external'");
   963                  renameExternalGroup.executeUpdate();
   964              }
   965              
   966              if (!con.getAutoCommit()) con.commit();
   967             
   968              successMessage("Planet group 'external' merged into group 'all'.");
   969              
   970          } catch (Exception e) {
   971              errorMessage("Problem upgrading database to version 400", e);
   972              throw new StartupException("Problem upgrading database to version 400", e);
   973          }
   974          
   975          
   976          // update local planet subscriptions to use new local feed format
   977          try {
   978              successMessage("Upgrading local planet subscription feeds to new feed url format");
   979              
   980              // need to start by looking up absolute site url
   981              PreparedStatement selectAbsUrl = 
   982                      con.prepareStatement("select value from roller_properties where name = 'site.absoluteurl'");
   983              String absUrl = null;
   984              ResultSet rs = selectAbsUrl.executeQuery();
   985              if(rs.next()) {
   986                  absUrl = rs.getString(1);
   987              }
   988              
   989              if(absUrl != null && absUrl.length() > 0) {
   990                  PreparedStatement selectSubs = 
   991                          con.prepareStatement("select id,feed_url,author from rag_subscription");
   992              
   993              PreparedStatement updateSubUrl = 
   994                      con.prepareStatement("update rag_subscription set last_updated=last_updated, feed_url = ? where id = ?");
   995              
   996              ResultSet rset = selectSubs.executeQuery();
   997              while (rset.next()) {
   998                  String id = rset.getString(1);
   999                  String feed_url = rset.getString(2);
  1000                  String handle = rset.getString(3);
  1001                  
  1002                  // only work on local feed urls
  1003                  if (feed_url.startsWith(absUrl)) {
  1004                      // update feed_url to 'weblogger:<handle>'
  1005                      updateSubUrl.clearParameters();
  1006                      updateSubUrl.setString( 1, "weblogger:"+handle);
  1007                      updateSubUrl.setString( 2, id);
  1008                      updateSubUrl.executeUpdate();
  1009                  }
  1010              }
  1011              }
  1012              
  1013              if (!con.getAutoCommit()) con.commit();
  1014             
  1015              successMessage("Comments successfully updated to use new comment plugins.");
  1016              
  1017          } catch (Exception e) {
  1018              errorMessage("Problem upgrading database to version 400", e);
  1019              throw new StartupException("Problem upgrading database to version 400", e);
  1020          }
  1021          
  1022          
  1023          // upgrade comments to use new plugin mechanism
  1024          try {
  1025              successMessage("Upgrading existing comments with content-type & plugins");
  1026              
  1027              // look in db and see if comment autoformatting is enabled
  1028              boolean autoformatEnabled = false;
  1029              String autoformat = null;
  1030              PreparedStatement selectIsAutoformtEnabled = con.prepareStatement(
  1031                  "select value from roller_properties where name = 'users.comments.autoformat'");
  1032              ResultSet rs = selectIsAutoformtEnabled.executeQuery();
  1033              if (rs.next()) {
  1034                  autoformat = rs.getString(1);
  1035                  if(autoformat != null && "true".equals(autoformat)) {
  1036                      autoformatEnabled = true;
  1037                  }
  1038              }
  1039              
  1040              // look in db and see if comment html escaping is enabled
  1041              boolean htmlEnabled = false;
  1042              String escapehtml = null;
  1043              PreparedStatement selectIsEscapehtmlEnabled = con.prepareStatement(
  1044                  "select value from roller_properties where name = 'users.comments.escapehtml'");
  1045              ResultSet rs1 = selectIsEscapehtmlEnabled.executeQuery();
  1046              if (rs1.next()) {
  1047                  escapehtml = rs1.getString(1);
  1048                  // NOTE: we allow html only when html escaping is OFF
  1049                  if(escapehtml != null && !"true".equals(escapehtml)) {
  1050                      htmlEnabled = true;
  1051                  }
  1052              }
  1053              
  1054              // first lets set the new 'users.comments.htmlenabled' property
  1055              PreparedStatement addCommentHtmlProp = con.prepareStatement("insert into roller_properties(name,value) values(?,?)");
  1056              addCommentHtmlProp.clearParameters();
  1057              addCommentHtmlProp.setString(1, "users.comments.htmlenabled");
  1058              if(htmlEnabled) {
  1059                  addCommentHtmlProp.setString(2, "true");
  1060              } else {
  1061                  addCommentHtmlProp.setString(2, "false");
  1062              }
  1063              addCommentHtmlProp.executeUpdate();
  1064              
  1065              // determine content-type for existing comments
  1066              String contentType = "text/plain";
  1067              if(htmlEnabled) {
  1068                  contentType = "text/html";
  1069              }
  1070              
  1071              // determine plugins for existing comments
  1072              String plugins = "";
  1073              if(htmlEnabled && autoformatEnabled) {
  1074                  plugins = "HTMLSubset,AutoFormat";
  1075              } else if(htmlEnabled) {
  1076                  plugins = "HTMLSubset";
  1077              } else if(autoformatEnabled) {
  1078                  plugins = "AutoFormat";
  1079              }
  1080              
  1081              // set new comment plugins configuration property 'users.comments.plugins'
  1082              PreparedStatement addCommentPluginsProp = 
  1083                      con.prepareStatement("insert into roller_properties(name,value) values(?,?)");
  1084              addCommentPluginsProp.clearParameters();
  1085              addCommentPluginsProp.setString(1, "users.comments.plugins");
  1086              addCommentPluginsProp.setString(2, plugins);
  1087              addCommentPluginsProp.executeUpdate();
  1088              
  1089              // set content-type for all existing comments
  1090              PreparedStatement updateCommentsContentType = 
  1091                      con.prepareStatement("update roller_comment set posttime=posttime, contenttype = ?");
  1092              updateCommentsContentType.clearParameters();
  1093              updateCommentsContentType.setString(1, contentType);
  1094              updateCommentsContentType.executeUpdate();
  1095  
  1096              // set plugins for all existing comments
  1097              PreparedStatement updateCommentsPlugins = 
  1098                      con.prepareStatement("update roller_comment set posttime=posttime, plugins = ?");
  1099              updateCommentsPlugins.clearParameters();
  1100              updateCommentsPlugins.setString(1, plugins);
  1101              updateCommentsPlugins.executeUpdate();
  1102              
  1103              if (!con.getAutoCommit()) con.commit();
  1104             
  1105              successMessage("Comments successfully updated to use new comment plugins.");
  1106              
  1107          } catch (Exception e) {
  1108              errorMessage("Problem upgrading database to version 400", e);
  1109              throw new StartupException("Problem upgrading database to version 400", e);
  1110          }
  1111          
  1112          // finally, upgrade db version string to 400
  1113          updateDatabaseVersion(con, 400);
  1114      }
  1115      
  1116      
  1117      /**
  1118       * Use database product name to get the database script directory name.
  1119       */
  1120      public String getDatabaseHandle(Connection con) throws SQLException {
  1121          
                 /* 
    P/P           *  Method: String getDatabaseHandle(Connection)
                  * 
                  *  Preconditions:
                  *    con != null
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:getMetaData(...)@1122 != null
                  *    java.sql.DatabaseMetaData:getDatabaseProductName(...)@1122 != null
                  * 
                  *  Postconditions:
                  *    return_value in Addr_Set{&"mysql",&"derby",&"oracle",&"db2",&"mssql",&"postgresql",&"hsqldb"}
                  * 
                  *  Test Vectors:
                  *    java.lang.String:indexOf(...)@1124: {-1}, {-231..-2, 0..232-1}
                  *    java.lang.String:indexOf(...)@1126: {-1}, {-231..-2, 0..232-1}
                  *    java.lang.String:indexOf(...)@1128: {-1}, {-231..-2, 0..232-1}
                  *    java.lang.String:indexOf(...)@1130: {-1}, {-231..-2, 0..232-1}
                  *    java.lang.String:indexOf(...)@1132: {-1}, {-231..-2, 0..232-1}
                  *    java.lang.String:indexOf(...)@1134: {-1}, {-231..-2, 0..232-1}
                  *    java.lang.String:indexOf(...)@1136: {-1}, {-231..-2, 0..232-1}
                  */
  1122          String productName = con.getMetaData().getDatabaseProductName();
  1123          String handle = "mysql";
  1124          if (       productName.toLowerCase().indexOf("mysql") != -1) {
  1125              handle =  "mysql";
  1126          } else if (productName.toLowerCase().indexOf("derby") != -1) {
  1127              handle =  "derby";
  1128          } else if (productName.toLowerCase().indexOf("hsql") != -1) {
  1129              handle =  "hsqldb";
  1130          } else if (productName.toLowerCase().indexOf("postgres") != -1) {
  1131              handle =  "postgresql";
  1132          } else if (productName.toLowerCase().indexOf("oracle") != -1) {
  1133              handle =  "oracle";
  1134          } else if (productName.toLowerCase().indexOf("microsoft") != -1) {
  1135              handle =  "mssql";
  1136          } else if (productName.toLowerCase().indexOf("db2") != -1) {   
  1137              handle =  "db2";
  1138          }
  1139          
  1140          return handle;
  1141      }
  1142  
  1143      
  1144      /**
  1145       * Return true if named table exists in database.
  1146       */
  1147      private boolean tableExists(Connection con, String tableName) throws SQLException {
                 /* 
    P/P           *  Method: bool tableExists(Connection, String)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    (soft) tableName != null
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:getMetaData(...)@1149 != null
                  *    java.sql.DatabaseMetaData:getTables(...)@1149 != null
                  *    java.sql.ResultSet:getString(...)@1151 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.lang.String:equals(...)@1151: {0}, {1}
                  *    java.sql.ResultSet:next(...)@1150: {0}, {1}
                  */
+ 1148          String[] types = {"TABLE"};
  1149          ResultSet rs = con.getMetaData().getTables(null, null, "%", null);
  1150          while (rs.next()) {
  1151              if (tableName.toLowerCase().equals(rs.getString("TABLE_NAME").toLowerCase())) {
  1152                  return true;
  1153              }
  1154          }
  1155          return false;
  1156      }
  1157      
  1158      
  1159      private int getDatabaseVersion() throws StartupException {
                 /* 
    P/P           *  Method: int getDatabaseVersion()
                  * 
                  *  Preconditions:
                  *    (soft) log != null
                  *    (soft) this.db != null
                  *    (soft) this.db.dataSource != null
                  *    (soft) init'ed(this.db.jdbcConnectionURL)
                  *    (soft) this.db.props != null
                  *    (soft) this.db.props._tainted == 0
                  *    (soft) init'ed(this.db.type)
                  * 
                  *  Presumptions:
                  *    java.sql.Connection:createStatement(...)@1166 != null
                  *    java.sql.Statement:executeQuery(...)@1169 != null
                  *    java.sql.Statement:executeQuery(...)@1179 != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    java.sql.ResultSet:next(...)@1172: {0}, {1}
                  */
  1160          int dbversion = -1;
  1161          
  1162          // get the current db version
  1163          Connection con = null;
  1164          try {
  1165              con = db.getConnection();
+ 1166              Statement stmt = con.createStatement();
  1167              
  1168              // just check in the roller_properties table
  1169              ResultSet rs = stmt.executeQuery(
  1170                      "select value from roller_properties where name = '"+DBVERSION_PROP+"'");
  1171              
  1172              if(rs.next()) {
  1173                  dbversion = Integer.parseInt(rs.getString(1));
  1174                  
  1175              } else {
  1176                  // tough to know if this is an upgrade with no db version :/
  1177                  // however, if roller_properties is not empty then we at least
  1178                  // we have someone upgrading from 1.2.x
  1179                  rs = stmt.executeQuery("select count(*) from roller_properties");
  1180                  if(rs.next()) {
  1181                      if(rs.getInt(1) > 0)
  1182                          dbversion = 120;
  1183                  }
  1184              }
  1185              
  1186          } catch(Exception e) {
  1187              // that's strange ... hopefully we didn't need to upgrade
  1188              log.error("Couldn't lookup current database version", e);           
  1189          } finally {
  1190              try { if (con != null) con.close(); } catch (Exception ignored) {}
  1191          }       
  1192          return dbversion;
  1193      }
  1194      
  1195      
  1196      /**
  1197       * Upgrade database for Roller 4.0.1
  1198       */
  1199      private void upgradeTo401(Connection con, boolean runScripts) throws StartupException {
                 /* 
    P/P           *  Method: void upgradeTo401(Connection, bool)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    (soft) org/apache/roller/weblogger/business/startup/SQLScriptRunner.log != null
                  *    (soft) this.messages != null
                  *    (soft) this.scripts != null
                  * 
                  *  Test Vectors:
                  *    runScripts: {0}, {1}
                  */
  1200          SQLScriptRunner runner = null;
  1201          try {
  1202              if (runScripts) {
  1203                  String handle = getDatabaseHandle(con);
  1204                  String scriptPath = handle + "/400-to-401-migration.sql";
  1205                  successMessage("Running database upgrade script: "+scriptPath);
  1206                  runner = new SQLScriptRunner(scripts.getDatabaseScript(scriptPath));
  1207                  runner.runScript(con, true);
  1208                  messages.addAll(runner.getMessages());
  1209              }
  1210          } catch (Exception e) {
  1211              log.error("ERROR running 401 database upgrade script", e);
  1212              if (runner != null) messages.addAll(runner.getMessages());
  1213  
  1214              errorMessage("Problem upgrading database to version 401", e);
  1215              throw new StartupException("Problem upgrading database to version 401", e);
  1216          }
  1217  
  1218          updateDatabaseVersion(con, 310);
  1219      }
  1220  
  1221  
  1222      private int parseVersionString(String vstring) {        
                 /* 
    P/P           *  Method: int parseVersionString(String)
                  * 
                  *  Preconditions:
                  *    vstring != null
                  * 
                  *  Presumptions:
                  *    java.lang.Integer:parseInt(...)@1236 >= -214_748_364
                  * 
                  *  Postconditions:
                  *    (soft) return_value >= -231+8
                  * 
                  *  Test Vectors:
                  *    java.lang.Integer:parseInt(...)@1236: {100..232-1}, {-214_748_364..99}
                  *    java.lang.String:length(...)@1231: {0..3}, {4..232-1}
                  */
  1223          int myversion = 0;
  1224          
  1225          // NOTE: this assumes a maximum of 3 digits for the version number
  1226          // so if we get to 10.0 then we'll need to upgrade this
  1227          
  1228          // strip out non-digits
  1229          vstring = vstring.replaceAll("\\Q.\\E", "");
  1230          vstring = vstring.replaceAll("\\D", "");
  1231          if(vstring.length() > 3)
  1232              vstring = vstring.substring(0, 3);
  1233          
  1234          // parse to an int
  1235          try {
  1236              int parsed = Integer.parseInt(vstring);            
  1237              if(parsed < 100) myversion = parsed * 10;
  1238              else myversion = parsed;
  1239          } catch(Exception e) {}  
  1240          
  1241          return myversion;
  1242      }
  1243      
  1244  
  1245      /**
  1246       * Insert a new database.version property.
  1247       * This should only be called once for new installations
  1248       */
  1249      private void setDatabaseVersion(Connection con, String version) 
  1250              throws StartupException {
                 /* 
    P/P           *  Method: void setDatabaseVersion(Connection, String)
                  * 
                  *  Preconditions:
                  *    con != null
                  *    log != null
                  *    version != null
                  */
  1251          setDatabaseVersion(con, parseVersionString(version));
  1252      }
  1253  
  1254      /**
  1255       * Insert a new database.version property.
  1256       * This should only be called once for new installations
  1257       */
  1258      private void setDatabaseVersion(Connection con, int version)
  1259              throws StartupException {
  1260          
  1261          try {
                     /* 
    P/P               *  Method: void setDatabaseVersion(Connection, int)
                      * 
                      *  Preconditions:
                      *    con != null
                      *    log != null
                      * 
                      *  Presumptions:
                      *    java.sql.Connection:createStatement(...)@1262 != null
                      */
  1262              Statement stmt = con.createStatement();
  1263              stmt.executeUpdate("insert into roller_properties "+
  1264                      "values('"+DBVERSION_PROP+"', '"+version+"')");
  1265              
  1266              log.debug("Set database verstion to "+version);
  1267          } catch(SQLException se) {
  1268              throw new StartupException("Error setting database version.", se);
  1269          }
  1270      }
  1271      
  1272      
  1273      /**
  1274       * Update the existing database.version property
  1275       */
  1276      private void updateDatabaseVersion(Connection con, int version)
  1277              throws StartupException {
  1278          
  1279          try {
                     /* 
    P/P               *  Method: void updateDatabaseVersion(Connection, int)
                      * 
                      *  Preconditions:
                      *    con != null
                      *    log != null
                      * 
                      *  Presumptions:
                      *    java.sql.Connection:createStatement(...)@1280 != null
                      */
  1280              Statement stmt = con.createStatement();
  1281              stmt.executeUpdate("update roller_properties "+
  1282                      "set value = '"+version+"'"+
  1283                      "where name = '"+DBVERSION_PROP+"'");
  1284              
  1285              log.debug("Updated database verstion to "+version);
  1286          } catch(SQLException se) {
  1287              throw new StartupException("Error setting database version.", se);
  1288          } 
  1289      }
  1290      
  1291  }








SofCheck Inspector Build Version : 2.18479
DatabaseInstaller.java 2009-Jan-02 14:25:30
DatabaseInstaller.class 2009-Sep-04 03:12:31