/* z * 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.storage.jdbc; import org.exoplatform.commons.utils.PrivilegedFileHelper; import org.exoplatform.commons.utils.PrivilegedSystemHelper; import org.exoplatform.commons.utils.SecurityHelper; import org.exoplatform.services.database.utils.DialectDetecter; import org.exoplatform.services.database.utils.JDBCUtils; import org.exoplatform.services.jcr.config.RepositoryConfigurationException; import org.exoplatform.services.jcr.config.RepositoryEntry; import org.exoplatform.services.jcr.config.SimpleParameterEntry; import org.exoplatform.services.jcr.config.ValueStorageEntry; import org.exoplatform.services.jcr.config.WorkspaceEntry; import org.exoplatform.services.jcr.dataflow.serialization.ObjectWriter; import org.exoplatform.services.jcr.impl.Constants; import org.exoplatform.services.jcr.impl.backup.BackupException; import org.exoplatform.services.jcr.impl.backup.Backupable; import org.exoplatform.services.jcr.impl.backup.ComplexDataRestore; import org.exoplatform.services.jcr.impl.backup.DataRestore; import org.exoplatform.services.jcr.impl.backup.rdbms.DBBackup; import org.exoplatform.services.jcr.impl.backup.rdbms.DBRestore; import org.exoplatform.services.jcr.impl.backup.rdbms.DataRestoreContext; import org.exoplatform.services.jcr.impl.backup.rdbms.DirectoryRestore; import org.exoplatform.services.jcr.impl.backup.rdbms.TableTransformationRule; import org.exoplatform.services.jcr.impl.backup.rdbms.TableTransformationRuleGenerator; import org.exoplatform.services.jcr.impl.clean.rdbms.DBCleanException; import org.exoplatform.services.jcr.impl.clean.rdbms.DBCleanService; import org.exoplatform.services.jcr.impl.clean.rdbms.DBCleanerTool; import org.exoplatform.services.jcr.impl.core.lock.cacheable.AbstractCacheableLockManager; import org.exoplatform.services.jcr.impl.core.query.NodeDataIndexingIterator; import org.exoplatform.services.jcr.impl.core.query.Reindexable; import org.exoplatform.services.jcr.impl.dataflow.SpoolConfig; import org.exoplatform.services.jcr.impl.dataflow.serialization.ObjectWriterImpl; import org.exoplatform.services.jcr.impl.storage.WorkspaceDataContainerBase; import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCDataContainerConfig.DatabaseStructureType; import org.exoplatform.services.jcr.impl.storage.jdbc.db.GenericConnectionFactory; import org.exoplatform.services.jcr.impl.storage.jdbc.db.HSQLDBConnectionFactory; import org.exoplatform.services.jcr.impl.storage.jdbc.db.MSSQLConnectionFactory; import org.exoplatform.services.jcr.impl.storage.jdbc.db.MySQLConnectionFactory; import org.exoplatform.services.jcr.impl.storage.jdbc.init.DB2DBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.init.HSQLDBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.init.MSSQLDBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.init.MysqlDBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.init.SybaseDBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.db.SybaseConnectionFactory; import org.exoplatform.services.jcr.impl.storage.jdbc.indexing.JdbcNodeDataIndexingIterator; import org.exoplatform.services.jcr.impl.storage.jdbc.init.IngresSQLDBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.init.OracleDBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.init.PgSQLDBInitializer; import org.exoplatform.services.jcr.impl.storage.jdbc.statistics.StatisticsJDBCStorageConnection; import org.exoplatform.services.jcr.impl.storage.value.StandaloneStoragePluginProvider; import org.exoplatform.services.jcr.impl.storage.value.fs.FileValueStorage; import org.exoplatform.services.jcr.impl.util.io.DirectoryHelper; import org.exoplatform.services.jcr.impl.util.io.FileCleanerHolder; import org.exoplatform.services.jcr.impl.util.jdbc.DBInitializer; import org.exoplatform.services.jcr.impl.util.jdbc.DBInitializerException; import org.exoplatform.services.jcr.impl.util.jdbc.DBInitializerHelper; import org.exoplatform.services.jcr.storage.WorkspaceDataContainer; import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection; import org.exoplatform.services.jcr.storage.value.ValueStoragePluginProvider; import org.exoplatform.services.jdbc.DataSourceProvider; import org.exoplatform.services.jdbc.impl.ManagedDataSource; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.naming.InitialContextInitializer; import org.picocontainer.Startable; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.jcr.RepositoryException; import javax.naming.NamingException; import javax.sql.DataSource; /** * Created by The eXo Platform SAS. * * @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter Nedonosko</a> * @version $Id:GenericWorkspaceDataContainer.java 13433 2007-03-15 16:07:23Z peterit $ */ public class JDBCWorkspaceDataContainer extends WorkspaceDataContainerBase implements Startable, Backupable, Reindexable { protected static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.JDBCWorkspaceDataContainer"); /** * Indicates if the statistics has to be enabled. */ public static final boolean STATISTICS_ENABLED = Boolean.valueOf(PrivilegedSystemHelper .getProperty("JDBCWorkspaceDataContainer.statistics.enabled")); static { if (STATISTICS_ENABLED) { LOG.info("The statistics of the component JDBCWorkspaceDataContainer has been enabled"); } } public final static String SOURCE_NAME = "source-name"; /** * Data structure type */ public final static String DB_STRUCTURE_TYPE = "db-structure-type"; /** * Use sequence for order number */ public final static String USE_SEQUENCE_FOR_ORDER_NUMBER = "use-sequence-for-order-number"; /** * Suffix used in tables names when isolated-databse structure used */ public final static String DB_TABLENAME_SUFFIX = "db-tablename-suffix"; /** * Describe which type of RDBMS will be used (DB creation metadata etc.) */ public final static String DB_DIALECT = "dialect"; /** * USE_SEQUENCE_AUTO. */ public final static String USE_SEQUENCE_AUTO = "auto"; public final static String DB_FORCE_QUERY_HINTS = "force.query.hints"; /** * Batch size value parameter name. */ public final static String BATCH_SIZE = "batch-size"; public final static int DEFAULT_BATCHING_DISABLED = -1; protected JDBCDataContainerConfig containerConfig; public GenericConnectionFactory connFactory; /** * Workspace configuration. */ protected final WorkspaceEntry wsConfig; /** * Constructor with value storage plugins. * * @param wsConfig * Workspace configuration * @param dsProvider * The data source provider * @throws RepositoryConfigurationException * if Repository configuration is wrong * @throws NamingException * if JNDI exception (on DataSource lookup) */ public JDBCWorkspaceDataContainer(WorkspaceEntry wsConfig, RepositoryEntry repConfig, InitialContextInitializer contextInit, ValueStoragePluginProvider valueStorageProvider, DataSourceProvider dsProvider, FileCleanerHolder fileCleanerHolder) throws RepositoryConfigurationException, NamingException, RepositoryException, IOException { checkIntegrity(wsConfig, repConfig); this.wsConfig = wsConfig; this.containerConfig = new JDBCDataContainerConfig(); this.containerConfig.containerName = wsConfig.getName(); this.containerConfig.uniqueName = wsConfig.getUniqueName(); this.containerConfig.dbStructureType = DBInitializerHelper.getDatabaseType(wsConfig); this.containerConfig.dbTableSuffix = DBInitializerHelper.getDBTableSuffix(wsConfig); this.containerConfig.valueStorageProvider = valueStorageProvider; this.containerConfig.dsProvider = dsProvider; this.containerConfig.batchSize = wsConfig.getContainer().getParameterInteger(BATCH_SIZE, DEFAULT_BATCHING_DISABLED); // ------------- Database config ------------------ String pDbDialect = validateDialect(DBInitializerHelper.getDatabaseDialect(wsConfig)); this.containerConfig.dbSourceName = wsConfig.getContainer().getParameterValue(SOURCE_NAME); if (dsProvider == null) { throw new IllegalArgumentException( "Since a data source has been defined, the DataSourceProvider cannot be null, add it in your configuration."); } // the data source cannot be managed if there is no transaction manager this.containerConfig.isManaged = dsProvider.isManaged(containerConfig.dbSourceName); if (pDbDialect.startsWith(DBConstants.DB_DIALECT_AUTO)) { // try to detect via JDBC metadata final DataSource ds = getDataSource(); Connection jdbcConn = null; try { jdbcConn = SecurityHelper.doPrivilegedSQLExceptionAction(new PrivilegedExceptionAction<Connection>() { public Connection run() throws Exception { return ds.getConnection(); } }); this.containerConfig.dbDialect = DialectDetecter.detect(jdbcConn.getMetaData()); } catch (SQLException e) { throw new RepositoryException(e); } finally { if (jdbcConn != null) { try { jdbcConn.close(); } catch (SQLException e) { throw new RepositoryException(e); } } } } else { this.containerConfig.dbDialect = pDbDialect; } // check is there DB_FORCE_QUERY_HINTS parameter - by default its enabled containerConfig.useQueryHints = wsConfig.getContainer().getParameterBoolean(DB_FORCE_QUERY_HINTS, true); try { this.containerConfig.checkSNSNewConnection = wsConfig.getContainer().getParameterBoolean(CHECK_SNS_NEW_CONNECTION); } catch (RepositoryConfigurationException e) { // don't use new connection by default this.containerConfig.checkSNSNewConnection = false; } try { if (wsConfig.getContainer().getParameterValue(USE_SEQUENCE_FOR_ORDER_NUMBER, USE_SEQUENCE_AUTO).equalsIgnoreCase(USE_SEQUENCE_AUTO)) { this.containerConfig.useSequenceForOrderNumber = useSequenceDefaultValue(); } else { this.containerConfig.useSequenceForOrderNumber = wsConfig.getContainer().getParameterBoolean(USE_SEQUENCE_FOR_ORDER_NUMBER); } } catch (RepositoryConfigurationException e) { this.containerConfig.useSequenceForOrderNumber = useSequenceDefaultValue(); } // ------------- Spool config ------------------ this.containerConfig.spoolConfig = new SpoolConfig(fileCleanerHolder.getFileCleaner()); try { this.containerConfig.spoolConfig.maxBufferSize = wsConfig.getContainer().getParameterInteger(MAXBUFFERSIZE_PROP); } catch (RepositoryConfigurationException e) { this.containerConfig.spoolConfig.maxBufferSize = DEF_MAXBUFFERSIZE; } try { String sdParam = wsConfig.getContainer().getParameterValue(SWAPDIR_PROP); this.containerConfig.spoolConfig.tempDirectory = new File(sdParam); } catch (RepositoryConfigurationException e1) { this.containerConfig.spoolConfig.tempDirectory = new File(DEF_SWAPDIR); } if (!PrivilegedFileHelper.exists(this.containerConfig.spoolConfig.tempDirectory)) { PrivilegedFileHelper.mkdirs(this.containerConfig.spoolConfig.tempDirectory); } else { cleanupSwapDirectory(); } this.containerConfig.initScriptPath = DBInitializerHelper.scriptPath(containerConfig.dbDialect, containerConfig.dbStructureType.isMultiDatabase()); LOG.info(getInfo()); initDatabase(); } /** * Deletes all the files from the swap directory */ private void cleanupSwapDirectory() { PrivilegedAction<Void> action = new PrivilegedAction<Void>() { public Void run() { File[] files = containerConfig.spoolConfig.tempDirectory.listFiles(); if (files != null && files.length > 0) { LOG.info("Some files have been found in the swap directory and will be deleted"); for (int i = 0; i < files.length; i++) { File file = files[i]; // We don't use the file cleaner in case the deletion failed to ensure // that the file won't be deleted while it is currently re-used file.delete(); } } return null; } }; SecurityHelper.doPrivilegedAction(action); } /** * Prepare default connection factory. * * @return GenericConnectionFactory * @throws NamingException * on JNDI error * @throws RepositoryException * on Storage error */ protected GenericConnectionFactory defaultConnectionFactory() throws NamingException, RepositoryException { return new GenericConnectionFactory(getDataSource(), containerConfig); } /** * Prepare default DB initializer. * * @return DBInitializer instance * @throws NamingException * on JNDI error * @throws RepositoryException * on Storage error * @throws IOException * on I/O error */ protected DBInitializer defaultDBInitializer() throws NamingException, RepositoryException, IOException { return new DBInitializer(this.connFactory.getJdbcConnection(), containerConfig); } /** * Checks if DataSources used in right manner. * * @param wsConfig * Workspace configuration * @param repConfig * Repository configuration * @throws RepositoryConfigurationException * in case of configuration errors */ protected void checkIntegrity(WorkspaceEntry wsConfig, RepositoryEntry repConfig) throws RepositoryConfigurationException { DatabaseStructureType dbType = DBInitializerHelper.getDatabaseType(wsConfig); for (WorkspaceEntry wsEntry : repConfig.getWorkspaceEntries()) { if (wsEntry.getName().equals(wsConfig.getName()) || !wsEntry.getContainer().getType().equals(wsConfig.getContainer().getType()) || !wsEntry.getContainer().getType().equals(this.getClass().getName())) { continue; } if (!DBInitializerHelper.getDatabaseType(wsEntry).equals(dbType)) { throw new RepositoryConfigurationException("All workspaces must be of same DB type. But " + wsEntry.getName() + "=" + DBInitializerHelper.getDatabaseType(wsEntry) + " and " + wsConfig.getName() + "=" + dbType); } // source name String wsSourceName = null; String newWsSourceName = null; try { wsSourceName = wsEntry.getContainer().getParameterValue("sourceName"); newWsSourceName = wsConfig.getContainer().getParameterValue("sourceName"); } catch (RepositoryConfigurationException e) { if (LOG.isTraceEnabled()) { LOG.trace("An exception occurred: " + e.getMessage()); } } if (wsSourceName != null && newWsSourceName != null) { if (dbType.isShareSameDatasource()) { if (!wsSourceName.equals(newWsSourceName)) { throw new RepositoryConfigurationException("SourceName must be equals in " + dbType + "-database repository." + " Check " + wsEntry.getName() + " and " + wsConfig.getName()); } } else { if (wsSourceName.equals(newWsSourceName)) { throw new RepositoryConfigurationException("SourceName " + wsSourceName + " already in use in " + wsEntry.getName() + ". SourceName must be different in " + dbType + "-database structure type. Check configuration for " + wsConfig.getName()); } } continue; } } } /** * Init storage database. * * @throws NamingException * on JNDI error * @throws RepositoryException * on storage error * @throws IOException * on I/O error */ protected void initDatabase() throws NamingException, RepositoryException, IOException { DBInitializer dbInitializer = null; if (containerConfig.dbDialect.equals(DBConstants.DB_DIALECT_ORACLEOCI)) { LOG.warn(DBConstants.DB_DIALECT_ORACLEOCI + " dialect is experimental!"); this.connFactory = defaultConnectionFactory(); dbInitializer = new OracleDBInitializer(this.connFactory.getJdbcConnection(), containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_ORACLE)) { this.connFactory = defaultConnectionFactory(); dbInitializer = new OracleDBInitializer(this.connFactory.getJdbcConnection(), containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_PGSQL)) { this.connFactory = defaultConnectionFactory(); dbInitializer = new PgSQLDBInitializer(this.connFactory.getJdbcConnection(), containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_MYSQL)) { if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_MYSQL_MYISAM)) { LOG.warn("MyISAM is not supported due to its lack of transaction support and integrity check, so use it only" + " if you don't expect any support and performances in read accesses are more important than the consistency" + " in your use-case. This dialect is only dedicated to the community."); } this.connFactory = new MySQLConnectionFactory(getDataSource(), containerConfig); dbInitializer = new MysqlDBInitializer(this.connFactory.getJdbcConnection(),containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_MSSQL)) { this.connFactory = new MSSQLConnectionFactory(getDataSource(), containerConfig); dbInitializer=new MSSQLDBInitializer(this.connFactory.getJdbcConnection(),containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_DERBY)) { this.connFactory = defaultConnectionFactory(); dbInitializer = defaultDBInitializer(); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_DB2)) { this.connFactory = defaultConnectionFactory(); dbInitializer = new DB2DBInitializer(this.connFactory.getJdbcConnection(),containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_SYBASE)) { this.connFactory = new SybaseConnectionFactory(getDataSource(), containerConfig); dbInitializer = new SybaseDBInitializer(this.connFactory.getJdbcConnection(),containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_INGRES)) { this.connFactory = defaultConnectionFactory(); dbInitializer = new IngresSQLDBInitializer(this.connFactory.getJdbcConnection(), containerConfig); } else if (containerConfig.dbDialect.startsWith(DBConstants.DB_DIALECT_HSQLDB)) { this.connFactory = new HSQLDBConnectionFactory(getDataSource(), containerConfig); dbInitializer = new HSQLDBInitializer(this.connFactory.getJdbcConnection(),containerConfig); } else { // generic, DB_HSQLDB this.connFactory = defaultConnectionFactory(); dbInitializer = defaultDBInitializer(); } // database type try { dbInitializer.init(); } catch (DBInitializerException e) { LOG.error("Error of init db " + e, e); } } /** * Return ConnectionFactory. * * @return WorkspaceStorageConnectionFactory connection */ protected GenericConnectionFactory getConnectionFactory() { return connFactory; } /** * {@inheritDoc} */ public WorkspaceStorageConnection openConnection() throws RepositoryException { WorkspaceStorageConnection con = connFactory.openConnection(); if (STATISTICS_ENABLED) { con = new StatisticsJDBCStorageConnection(con); } return con; } /** * {@inheritDoc} */ public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException { WorkspaceStorageConnection con = connFactory.openConnection(readOnly); if (STATISTICS_ENABLED) { con = new StatisticsJDBCStorageConnection(con); } return con; } /** * {@inheritDoc} */ public WorkspaceStorageConnection reuseConnection(WorkspaceStorageConnection original) throws RepositoryException { if (original instanceof StatisticsJDBCStorageConnection) { original = ((StatisticsJDBCStorageConnection)original).getNestedWorkspaceStorageConnection(); } if (original instanceof JDBCStorageConnection) { final JDBCStorageConnection storageConnection = (JDBCStorageConnection)original; final Connection connection = storageConnection.getJdbcConnection(); storageConnection.share(); final Connection proxyConnection = (Connection)Proxy.newProxyInstance(GenericConnectionFactory.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("close".equals(method.getName()) && storageConnection.release() > 0) { // We don't close the connection as long as it is used return null; } else if (("commit".equals(method.getName()) || "rollback".equals(method.getName())) && storageConnection.getDbConnectionTotalUsed() > 1) { // We don't commit or roll back as long as it is used return null; } return method.invoke(connection, args); } }); GenericConnectionFactory cFactory = connFactory.cloneWith((DataSource)Proxy.newProxyInstance(GenericConnectionFactory.class.getClassLoader(), new Class[]{DataSource.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("getConnection".equals(method.getName())) { return proxyConnection; } return method.invoke(getDataSource(), args); } })); return STATISTICS_ENABLED ? new StatisticsJDBCStorageConnection(cFactory.openConnection(false)) : cFactory .openConnection(false); } else { return openConnection(false); } } /** * {@inheritDoc} */ public String getName() { return containerConfig.containerName; } /** * {@inheritDoc} */ public String getUniqueName() { return containerConfig.uniqueName; } /** * {@inheritDoc} */ public String getInfo() { StringBuilder builder = new StringBuilder(); builder.append("dialect:"); builder.append(this.containerConfig.dbDialect); builder.append(", "); for (SimpleParameterEntry element : wsConfig.getContainer().getParameters()) { if (!element.getName().equals("dialect")) { builder.append(element.getName()); builder.append(":"); builder.append(element.getValue()); builder.append(", "); } } builder.append("value storage provider: "); builder.append(containerConfig.valueStorageProvider); return builder.toString(); } /** * {@inheritDoc} */ public String getStorageVersion() { return containerConfig.storageVersion; } /** * {@inheritDoc} */ public void start() { // if isolation level lesser then TRANSACTION_READ_COMMITTED, print a warning Connection con = null; try { con = getConnectionFactory().getJdbcConnection(); if (con.getTransactionIsolation() < Connection.TRANSACTION_READ_COMMITTED) { LOG.warn("Wrong default isolation level, please set the default isolation level " + "to READ_COMMITTED or higher. Other default isolation levels are not supported"); } } catch (SQLException e) { LOG.error("Error checking isolation level configuration.", e); } catch (RepositoryException e) { LOG.error("Error checking isolation level configuration.", e); } finally { if (con != null) { try { con.close(); } catch (SQLException e) { if (LOG.isTraceEnabled()) { LOG.trace("An exception occurred: " + e.getMessage()); } } } } // Remove lock properties from DB. It is an issue of migration locks from 1.12.x to 1.14.x in case when we use // shareable cache. The lock tables will be new but still remaining lock properties in JCR tables. boolean deleteLocks = "true".equalsIgnoreCase(PrivilegedSystemHelper.getProperty(AbstractCacheableLockManager.LOCKS_FORCE_REMOVE, "false")); try { if (deleteLocks) { boolean failed = true; WorkspaceStorageConnection wsc = openConnection(false); if (wsc instanceof StatisticsJDBCStorageConnection) { wsc = ((StatisticsJDBCStorageConnection)wsc).getNestedWorkspaceStorageConnection(); } JDBCStorageConnection conn = (JDBCStorageConnection)wsc; try { conn.deleteLockProperties(); conn.commit(); failed = false; } finally { if (failed) { conn.rollback(); } } } } catch (SQLException e) { LOG.error("Can't remove lock properties because of " + JDBCUtils.getFullMessage(e), e); } catch (RepositoryException e) { LOG.error("Can't remove lock properties because of " + e.getMessage(), e); } } /** * {@inheritDoc} */ public void stop() { } /** * {@inheritDoc} */ public boolean isSame(WorkspaceDataContainer another) { if (another == this) { return true; } if (another instanceof JDBCWorkspaceDataContainer) { JDBCWorkspaceDataContainer anotherJdbc = (JDBCWorkspaceDataContainer)another; return getDbSourceName().equals(anotherJdbc.getDbSourceName()); } return false; } /** * Used in <code>equals()</code>. * * @return DataSource name */ protected String getDbSourceName() { return containerConfig.dbSourceName; } /** * {@inheritDoc} */ public boolean isCheckSNSNewConnection() { return containerConfig.checkSNSNewConnection; } /** * {@inheritDoc} */ public void clean() throws BackupException { LOG.info("Start to clean value storage of the workspace '"+containerConfig.containerName+"'"); try { DBCleanService.cleanWorkspaceData(wsConfig); if (wsConfig.getContainer().getValueStorages() != null) { SecurityHelper.doPrivilegedExceptionAction(new PrivilegedExceptionAction<Void>() { public Void run() throws IOException, RepositoryConfigurationException { for (ValueStorageEntry valueStorage : wsConfig.getContainer().getValueStorages()) { File valueStorageDir = new File(valueStorage.getParameterValue(FileValueStorage.PATH)); if (valueStorageDir.exists()) { DirectoryHelper.removeDirectory(valueStorageDir); } } return null; } }); } } catch (DBCleanException e) { throw new BackupException(e); } catch (PrivilegedActionException e) { throw new BackupException(e); } } /** * {@inheritDoc} */ public void backup(final File storageDir) throws BackupException { LOG.info("Start to backup value storage of the workspace '"+containerConfig.containerName+"'"); ObjectWriter backupInfo = null; try { backupInfo = new ObjectWriterImpl(PrivilegedFileHelper.fileOutputStream(new File(storageDir, "JDBCWorkspaceDataContainer.info"))); backupInfo.writeString(containerConfig.containerName); backupInfo.writeString(containerConfig.dbStructureType.toString()); Map<String, String> scripts = new HashMap<String, String>(); String itemTable = DBInitializerHelper.getItemTableName(containerConfig); String valueTable = DBInitializerHelper.getValueTableName(containerConfig); String refTable = DBInitializerHelper.getRefTableName(containerConfig); backupInfo.writeString(itemTable); backupInfo.writeString(valueTable); backupInfo.writeString(refTable); if (containerConfig.dbStructureType.isMultiDatabase()) { scripts .put(itemTable, "select * from " + itemTable + " where NAME <> '" + Constants.ROOT_PARENT_NAME + "'"); scripts.put(valueTable, "select * from " + valueTable); scripts.put(refTable, "select * from " + refTable); } else { scripts.put(itemTable, "select * from " + itemTable + " where CONTAINER_NAME='" + containerConfig.containerName + "'"); scripts.put(valueTable, "select V.* from " + valueTable + " V, " + itemTable + " I where I.ID=V.PROPERTY_ID and I.CONTAINER_NAME='" + containerConfig.containerName + "'"); scripts.put(refTable, "select R.* from " + refTable + " R, " + itemTable + " I where I.ID=R.PROPERTY_ID and I.CONTAINER_NAME='" + containerConfig.containerName + "'"); } // using existing DataSource to get a JDBC Connection. Connection jdbcConn = connFactory.getJdbcConnection(); DBBackup.backup(storageDir, jdbcConn, scripts); // backup value storage if (wsConfig.getContainer().getValueStorages() != null) { SecurityHelper.doPrivilegedExceptionAction(new PrivilegedExceptionAction<Void>() { public Void run() throws RepositoryConfigurationException, IOException { for (ValueStorageEntry valueStorage : wsConfig.getContainer().getValueStorages()) { Boolean enable = valueStorage.getParameterBoolean(StandaloneStoragePluginProvider.VALUE_STORAGE_ENABLED_PARAM, true); if (enable) { File srcDir = new File(valueStorage.getParameterValue(FileValueStorage.PATH)); if (!srcDir.exists()) { throw new IOException("Can't backup value storage. Directory " + srcDir.getName() + " doesn't exist"); } else { File zipFile = new File(storageDir, "values-" + valueStorage.getId() + ".zip"); DirectoryHelper.compressDirectory(srcDir, zipFile); } } } return null; } }); } } catch (IOException e) { throw new BackupException(e); } catch (RepositoryException e) { throw new BackupException(e); } catch (PrivilegedActionException e) { throw new BackupException(e); } finally { if (backupInfo != null) { try { backupInfo.close(); } catch (IOException e) { throw new BackupException(e); } } } } /** * {@inheritDoc} */ public DataRestore getDataRestorer(DataRestoreContext dataRestoreContext) throws BackupException { try { List<DataRestore> restorers = new ArrayList<DataRestore>(); Map<String, TableTransformationRule> tables = new LinkedHashMap<String, TableTransformationRule>(); Connection jdbcConn = getJdbcConnection(dataRestoreContext); DBCleanerTool dbCleaner = getDbCleaner(dataRestoreContext, jdbcConn); File storageDir = getStorageDir(dataRestoreContext); TableTransformationRuleGenerator tableTransformationRuleGenerator = new TableTransformationRuleGenerator(containerConfig, storageDir); tables.put(DBInitializerHelper.getItemTableName(containerConfig), tableTransformationRuleGenerator.getItemTableTransformationRule()); tables.put(DBInitializerHelper.getValueTableName(containerConfig), tableTransformationRuleGenerator.getValueTableTransformationRule()); tables.put(DBInitializerHelper.getRefTableName(containerConfig), tableTransformationRuleGenerator.getRefTableTransformationRule()); restorers.add(new DBRestore(storageDir, jdbcConn, tables, wsConfig, containerConfig.spoolConfig.fileCleaner, dbCleaner)); if (wsConfig.getContainer().getValueStorages() != null) { List<File> dataDirsList = initDataDirs(); List<File> backupDirsList = initBackupDirs(storageDir); if (!(dataDirsList.isEmpty() && backupDirsList.isEmpty())) { restorers.add(new DirectoryRestore(dataDirsList, backupDirsList)); } } return new ComplexDataRestore(restorers); } catch (IOException e) { throw new BackupException(e); } catch (NamingException e) { throw new BackupException(e); } catch (SQLException e) { throw new BackupException(e); } catch (RepositoryConfigurationException e) { throw new BackupException(e); } } private List<File> initBackupDirs(File storageDir) throws RepositoryConfigurationException { List<File> backupDirsList = new ArrayList<File>(); for (ValueStorageEntry valueStorage : wsConfig.getContainer().getValueStorages()) { Boolean enable = valueStorage.getParameterBoolean(StandaloneStoragePluginProvider.VALUE_STORAGE_ENABLED_PARAM, true); if (enable) { File zipFile = new File(storageDir, "values-" + valueStorage.getId() + ".zip"); if (PrivilegedFileHelper.exists(zipFile)) { backupDirsList.add(zipFile); } else { // try to check if we have deal with old backup format zipFile = new File(storageDir, "values/" + valueStorage.getId()); if (PrivilegedFileHelper.exists(zipFile)) { backupDirsList.add(zipFile); } else { throw new RepositoryConfigurationException("There is no backup data for value storage with id " + valueStorage.getId()); } } } } return backupDirsList; } private List<File> initDataDirs() throws RepositoryConfigurationException { List<File> dataDirsList = new ArrayList<File>(); for (ValueStorageEntry valueStorage : wsConfig.getContainer().getValueStorages()) { Boolean enable = valueStorage.getParameterBoolean(StandaloneStoragePluginProvider.VALUE_STORAGE_ENABLED_PARAM, true); if (enable) { File dataDir = new File(valueStorage.getParameterValue(FileValueStorage.PATH)); dataDirsList.add(dataDir); } } return dataDirsList; } private DBCleanerTool getDbCleaner(DataRestoreContext context, Connection jdbcConn) throws BackupException { DBCleanerTool dbCleaner; if (context.getObject(DataRestoreContext.DB_CLEANER) != null) { dbCleaner = (DBCleanerTool)context.getObject(DataRestoreContext.DB_CLEANER); } else { try { dbCleaner = DBCleanService.getWorkspaceDBCleaner(jdbcConn, wsConfig); } catch (DBCleanException e) { throw new BackupException(e); } } return dbCleaner; } private File getStorageDir(DataRestoreContext context) { return (File)context.getObject(DataRestoreContext.STORAGE_DIR); } private Connection getJdbcConnection(DataRestoreContext context) throws BackupException { Connection jdbcConnection = null; if (context.getObject(DataRestoreContext.DB_CONNECTION) == null) { try { jdbcConnection = connFactory.getJdbcConnection(); jdbcConnection.setAutoCommit(false); } catch (SQLException e) { throw new BackupException(e); } catch (RepositoryException e) { throw new BackupException(e); } } else { jdbcConnection = (Connection)context.getObject(DataRestoreContext.DB_CONNECTION); } return jdbcConnection; } /** * {@inheritDoc} */ public NodeDataIndexingIterator getNodeDataIndexingIterator(int pageSize) throws RepositoryException { if (isReindexingSupported()) { return new JdbcNodeDataIndexingIterator(connFactory, pageSize); } throw new UnsupportedOperationException( "The method getNodeDataIndexingIterator is not supported for this type of connection " + "use the complex queries instead"); } /** * {@inheritDoc} */ public boolean isReindexingSupported() { return connFactory.isReindexingSupported(); } /** * {@inheritDoc} */ public Long getNodesCount() throws RepositoryException { WorkspaceStorageConnection conn = connFactory.openConnection(); try { return conn.getNodesCount(); } finally { conn.close(); } } /** * Get the data source from the InitialContext and wraps it into a {@link ManagedDataSource} * in case it has been configured as managed */ protected DataSource getDataSource() throws RepositoryException { try { return containerConfig.dsProvider.getDataSource(containerConfig.dbSourceName); } catch (NamingException e) { throw new RepositoryException("Datasource '" + containerConfig.dbSourceName + "' is not bound in this context.", e); } } /** * use-sequence value according to dialect */ public static boolean useSequenceDefaultValue() { return false; } /** * Validate dialect. * * @param confParam * String, dialect from configuration. * @return String * return dialect. By default return DB_DIALECT_GENERIC. * */ private String validateDialect(String confParam) { for (String dbType : DBConstants.DB_DIALECTS) { if (confParam.equals(dbType)) { return dbType; } } return DBConstants.DB_DIALECT_AUTO; // by default } }