/* * Copyright (C) 2003-2007 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see. */ package org.exoplatform.services.database.jdbc; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.exoplatform.services.log.Log; import org.exoplatform.container.component.ComponentPlugin; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.naming.InitialContextInitializer; /** * Created by The eXo Platform SAS . * * @author Gennady * Azarenkov * @version $Id: DBSchemaCreator.java 13053 2007-03-01 06:44:00Z tuan08 $ */ public class DBSchemaCreator { static public String SQL_DELIMITER_COMMENT_PREFIX = "/*$DELIMITER:"; static public String SQL_DELIMITER = ";"; static private String SQL_ALREADYEXISTS = ".*((already exist)|(duplicate key)| (already used)|(ORA-00955))+.*"; private final Pattern pattern; private static Log log = ExoLogger.getLogger("database.DBSchemaCreator"); private List createDBSchemaPlugins = new ArrayList(); @SuppressWarnings("unused") public DBSchemaCreator(InitialContextInitializer contextInit) { pattern = Pattern.compile(SQL_ALREADYEXISTS, Pattern.CASE_INSENSITIVE); } // for testing only private DBSchemaCreator(String dsName, String script) throws SQLException, NamingException { pattern = Pattern.compile(SQL_ALREADYEXISTS, Pattern.CASE_INSENSITIVE); createTables(dsName, script); } public void createTables(String dsName, String script) throws NamingException, SQLException { InitialContext context = new InitialContext(); DataSource ds = (DataSource) context.lookup(dsName); Connection conn = ds.getConnection(); String sql = ""; try { String[] scripts = null; if (script.startsWith(SQL_DELIMITER_COMMENT_PREFIX)) { // read custom prefix try { String s = script.substring(SQL_DELIMITER_COMMENT_PREFIX.length()); int endOfDelimIndex = s.indexOf("*/"); String delim = s.substring(0, endOfDelimIndex).trim(); s = s.substring(endOfDelimIndex + 2).trim(); scripts = s.split(delim); } catch (IndexOutOfBoundsException e) { log.warn("Error of parse SQL-script file. Invalid DELIMITER configuration. Valid format is '" + SQL_DELIMITER_COMMENT_PREFIX + "XXX*/' at begin of the SQL-script file, where XXX - DELIMITER string." + " Spaces will be trimed. ", e); log.info("Using DELIMITER:[" + SQL_DELIMITER + "]"); scripts = script.split(SQL_DELIMITER); } } else { scripts = script.split(SQL_DELIMITER); } for (String scr : scripts) { String s = cleanWhitespaces(scr.trim()); if (s.length() < 1) continue; sql = s; if (log.isDebugEnabled()) log.debug("Execute script: \n[" + sql + "]"); try { conn.setAutoCommit(false); conn.createStatement().executeUpdate(sql); conn.commit(); } catch (SQLException e) { conn.rollback(); // already exists check Matcher aeMatcher = pattern.matcher(e.getMessage().trim()); if (!aeMatcher.matches()) throw e; if (log.isDebugEnabled()) log.debug(e.getMessage()); } } log.info("DB schema of DataSource: '" + dsName + "' created succesfully. context " + context); } catch (SQLException e) { SQLException next = e.getNextException(); String errorTrace = ""; while (next != null) { errorTrace += next.getMessage() + "; "; next = e.getNextException(); } Throwable cause = e.getCause(); log.error("Could not create db schema of DataSource: '" + dsName + "'. Reason: " + e.getMessage() + "; " + errorTrace + (cause != null ? " (Cause: " + cause.getMessage() + ")" : "") + ". Last command: " + sql); e.printStackTrace(); } finally { conn.close(); } } public void addPlugin(ComponentPlugin plugin) { if (plugin instanceof CreateDBSchemaPlugin) { CreateDBSchemaPlugin csplugin = (CreateDBSchemaPlugin) plugin; try { createTables(csplugin.getDataSource(), csplugin.getScript()); createDBSchemaPlugins.add(csplugin); } catch (NamingException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } @SuppressWarnings("unused") public ComponentPlugin removePlugin(String name) { return null; } public Collection getPlugins() { return createDBSchemaPlugins; } // for testing public static DBSchemaCreator initialize(String dsName, String script) throws SQLException, NamingException { return new DBSchemaCreator(dsName, script); } static public String cleanWhitespaces(String string) { if (string == null || string.length() < 1) return string; char[] cc = string.toCharArray(); for (int ci = cc.length - 1; ci > 0; ci--) { if (Character.isWhitespace(cc[ci])) cc[ci] = ' '; } return new String(cc); } }