/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.etk.component.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.etk.common.logging.Logger;
import org.etk.component.base.naming.InitialContextInitializer;
import org.etk.kernel.container.component.ComponentPlugin;
/**
* Created by The eXo Platform SAS .
*
* @author <a href="mailto:gennady.azarenkov@exoplatform.com">Gennady
* Azarenkov</a>
* @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 Logger log = Logger.getLogger(DBSchemaCreator.class);
private List<CreateDBSchemaPlugin> createDBSchemaPlugins = new ArrayList<CreateDBSchemaPlugin>();
@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 = next.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);
}
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)
{
log.error(e.getLocalizedMessage(), e);
}
catch (SQLException e)
{
log.error(e.getLocalizedMessage(), e);
}
}
}
@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);
}
}