/* * 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.exoplatform.services.jcr.impl.config; import org.exoplatform.commons.utils.SecurityHelper; import org.exoplatform.container.xml.PropertiesParam; import org.exoplatform.services.database.utils.DialectDetecter; import org.exoplatform.services.database.utils.JDBCUtils; import org.exoplatform.services.jcr.config.ConfigurationPersister; import org.exoplatform.services.jcr.config.RepositoryConfigurationException; import org.exoplatform.services.jcr.impl.storage.jdbc.DBConstants; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; /** * Repository service configuration persister. * * @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter Nedonosko</a> * @version $Id: JDBCConfigurationPersister.java 11907 2008-03-13 15:36:21Z ksm $ */ public class JDBCConfigurationPersister implements ConfigurationPersister { protected static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.JDBCConfigurationPersister"); public final static String PARAM_SOURCE_NAME = "source-name"; public final static String PARAM_DIALECT = "dialect"; protected static final String CONFIGNAME = "REPOSITORY-SERVICE-WORKING-CONFIG"; protected static final String C_DATA = "CONFIGDATA"; protected String configTableName = "JCR_CONFIG"; protected String sourceName; protected String initSQL; public class ConfigurationNotFoundException extends RepositoryConfigurationException { ConfigurationNotFoundException(String m) { super(m); } } public class ConfigurationNotInitializedException extends RepositoryConfigurationException { ConfigurationNotInitializedException(String m) { super(m); } } protected class ConfigDataHolder { private final byte[] config; ConfigDataHolder(InputStream source) throws IOException { ByteArrayOutputStream configOut = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int read = 0; while ((read = source.read(b)) > 0) { configOut.write(b, 0, read); } this.config = configOut.toByteArray(); } InputStream getStream() { return new ByteArrayInputStream(config); } int getLength() { return config.length; } } public void init(PropertiesParam params) throws RepositoryConfigurationException { String sourceNameParam = params.getProperty(PARAM_SOURCE_NAME); if (sourceNameParam == null) { sourceNameParam = params.getProperty("sourceName"); // try old, pre 1.9 name if (sourceNameParam == null) { throw new RepositoryConfigurationException("Repository service configuration. Source name (" + PARAM_SOURCE_NAME + ") is expected"); } } this.sourceName = sourceNameParam; String dialectParam = params.getProperty(PARAM_DIALECT); String dialect = dialectParam == null ? DBConstants.DB_DIALECT_AUTO : dialectParam.toUpperCase(); if (dialect.startsWith(DBConstants.DB_DIALECT_AUTO)) { Connection conn = null; try { conn = openConnection(); dialect = DialectDetecter.detect(conn.getMetaData()); } catch (NamingException e) { throw new RepositoryConfigurationException(e); } catch (SQLException e) { throw new RepositoryConfigurationException(e); } finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { throw new RepositoryConfigurationException(e); } } } } String binType = "BLOB"; if (dialect.startsWith(DBConstants.DB_DIALECT_GENERIC) || dialect.startsWith(DBConstants.DB_DIALECT_HSQLDB)) { binType = "VARBINARY(1000000)"; // 1Mb } else if (dialect.startsWith(DBConstants.DB_DIALECT_PGSQL)) { configTableName = configTableName.toUpperCase().toLowerCase(); // postgres needs it binType = "BYTEA"; } else if (dialect.startsWith(DBConstants.DB_DIALECT_MSSQL)) { binType = "VARBINARY(max)"; } else if (dialect.startsWith(DBConstants.DB_DIALECT_SYBASE)) { binType = "IMAGE"; } else if (dialect.startsWith(DBConstants.DB_DIALECT_INGRES)) { configTableName = configTableName.toUpperCase().toLowerCase(); // ingres needs it binType = "LONG BYTE"; } else if (dialect.startsWith(DBConstants.DB_DIALECT_MYSQL)) { binType = "LONGBLOB"; } this.initSQL = "CREATE TABLE " + configTableName + " (" + "NAME VARCHAR(64) NOT NULL, " + "CONFIG " + binType + " NOT NULL, " + "CONSTRAINT JCR_CONFIG_PK PRIMARY KEY(NAME))"; } protected void checkInitialized() throws RepositoryConfigurationException { if (sourceName == null) { throw new RepositoryConfigurationException( "Repository service configuration persister isn not initialized. Call init() before."); } } protected Connection openConnection() throws NamingException, SQLException { final DataSource ds = (DataSource)new InitialContext().lookup(sourceName); return SecurityHelper.doPrivilegedSQLExceptionAction(new PrivilegedExceptionAction<Connection>() { public Connection run() throws Exception { return ds.getConnection(); } }); } /** * Check if config table already exists * * @param con */ protected boolean isDbInitialized(final Connection con) { return SecurityHelper.doPrivilegedAction(new PrivilegedAction<Boolean>() { public Boolean run() { return JDBCUtils.tableExists(configTableName, con); } }); } public boolean hasConfig() throws RepositoryConfigurationException { checkInitialized(); try { Connection con = openConnection(); ResultSet res = null; PreparedStatement ps = null; try { if (isDbInitialized(con)) { // check that data exists ps = con.prepareStatement("SELECT COUNT(*) FROM " + configTableName + " WHERE NAME=?"); ps.setString(1, CONFIGNAME); res = ps.executeQuery(); if (res.next()) { return res.getInt(1) > 0; } } return false; } finally { if (res != null) { try { res.close(); } catch (SQLException e) { LOG.error("Can't close the ResultSet: " + e.getMessage()); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { LOG.error("Can't close the Statement: " + e.getMessage()); } } con.close(); } } catch (final SQLException e) { throw new RepositoryConfigurationException("Database exception. " + e, e); } catch (final NamingException e) { throw new RepositoryConfigurationException("JDNI exception. " + e, e); } } public InputStream read() throws RepositoryConfigurationException { checkInitialized(); try { Connection con = openConnection(); ResultSet res = null; PreparedStatement ps = null; try { if (isDbInitialized(con)) { ps = con.prepareStatement("SELECT * FROM " + configTableName + " WHERE NAME=?"); ps.setString(1, CONFIGNAME); res = ps.executeQuery(); if (res.next()) { ConfigDataHolder config = new ConfigDataHolder(res.getBinaryStream("config")); return config.getStream(); } else { throw new ConfigurationNotFoundException("No configuration data is found in database. Source name " + sourceName); } } else { throw new ConfigurationNotInitializedException( "Configuration table not is found in database. Source name " + sourceName); } } finally { if (res != null) { try { res.close(); } catch (SQLException e) { LOG.error("Can't close the ResultSet: " + e.getMessage()); } } if (ps != null) { try { ps.close(); } catch (SQLException e) { LOG.error("Can't close the Statement: " + e.getMessage()); } } con.close(); } } catch (final IOException e) { throw new RepositoryConfigurationException("Configuration read exception. " + e, e); } catch (final SQLException e) { throw new RepositoryConfigurationException("Database exception. " + e, e); } catch (final NamingException e) { throw new RepositoryConfigurationException("JDNI exception. " + e, e); } } public void write(InputStream confData) throws RepositoryConfigurationException { checkInitialized(); String sql = null; try { Connection con = openConnection(); PreparedStatement ps = null; try { if (!isDbInitialized(con)) { // init db con.setAutoCommit(true); Statement st = con.createStatement(); st.executeUpdate(sql = initSQL); st.close(); con.close(); // one new conn con = openConnection(); } con.setAutoCommit(false); if (isDbInitialized(con)) { ConfigDataHolder config = new ConfigDataHolder(confData); if (hasConfig()) { sql = "UPDATE " + configTableName + " SET CONFIG=? WHERE NAME=?"; ps = con.prepareStatement(sql); ps.setBinaryStream(1, config.getStream(), config.getLength()); ps.setString(2, CONFIGNAME); } else { sql = "INSERT INTO " + configTableName + " (NAME, CONFIG) VALUES (?,?)"; ps = con.prepareStatement(sql); ps.setString(1, CONFIGNAME); ps.setBinaryStream(2, config.getStream(), config.getLength()); } if (ps.executeUpdate() <= 0) { LOG.warn("Repository service configuration doesn't stored ok. " + "No rows was affected in JDBC operation. Datasource " + sourceName + ". SQL: " + sql); } } else { throw new ConfigurationNotInitializedException( "Configuration table can not be created in database. Source name " + sourceName + ". SQL: " + sql); } con.commit(); } finally { if (ps != null) { try { ps.close(); } catch (SQLException e) { LOG.error("Can't close the Statement: " + e.getMessage()); } } con.close(); } } catch (final IOException e) { throw new RepositoryConfigurationException("Configuration read exception. " + e, e); } catch (final SQLException e) { throw new RepositoryConfigurationException("Database exception. " + e + ". SQL: " + sql, e); } catch (final NamingException e) { throw new RepositoryConfigurationException("JDNI exception. " + e, e); } } }