diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml index 387785b335..f8cefc9971 100644 --- a/config/alfresco/dao/dao-context.xml +++ b/config/alfresco/dao/dao-context.xml @@ -3,6 +3,32 @@ + + + controlDAO.#bean.dialect# + + + org.alfresco.repo.domain.control.ControlDAO + + + org.hibernate.dialect.Dialect + + + + + + + + + + + + + + diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index 93a1a303a2..16465c2553 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -314,6 +314,7 @@ patch.thumbnailsAssocQName.description=Update the 'cm:thumbnails' association QN patch.convertContentUrls.description=Converts pre-3.2 content URLs to use the alf_content_data table. The conversion work can also be done on a schedule; please contact Alfresco Support for further details. patch.convertContentUrls.bypassingPatch=Content URL conversion was NOT performed by this patch. Activate the scheduled job 'contentUrlConverterTrigger'. patch.convertContentUrls.start=Content URL conversion progress: +patch.convertContentUrls.error=Content URL conversion failed: {0} patch.convertContentUrls.inProgress=Content URL conversion increment completed. Awaiting next scheduled call... patch.convertContentUrls.done=Content URL conversion completed. patch.convertContentUrls.adm.start=\tProcessing ADM Content URLs. diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 727b0ecea5..a4bc459a26 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -2138,6 +2138,9 @@ + + + diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ContentUrlConverterPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ContentUrlConverterPatch.java index b043a38860..9b8284fe47 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/ContentUrlConverterPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/ContentUrlConverterPatch.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.admin.patch.impl; +import java.sql.Savepoint; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -34,6 +35,7 @@ import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorkerAdaptor; import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.ContentStore.ContentUrlHandler; import org.alfresco.repo.domain.contentdata.ContentDataDAO; +import org.alfresco.repo.domain.control.ControlDAO; import org.alfresco.repo.domain.patch.PatchDAO; import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.lock.LockAcquisitionException; @@ -106,6 +108,7 @@ public class ContentUrlConverterPatch extends AbstractPatch private JobLockService jobLockService; private NodeDaoService nodeDaoService; private PatchDAO patchDAO; + private ControlDAO controlDAO; private ContentStore contentStore; private ContentDataDAO contentDataDAO; private int threadCount; @@ -156,6 +159,14 @@ public class ContentUrlConverterPatch extends AbstractPatch this.patchDAO = patchDAO; } + /** + * Component that provides low-level database-specific control to support the patch + */ + public void setControlDAO(ControlDAO controlDAO) + { + this.controlDAO = controlDAO; + } + /** * Set the store containing the content URLs to lift for potential cleaning. * @@ -307,6 +318,13 @@ public class ContentUrlConverterPatch extends AbstractPatch completed = admCompleted && avmCompleted && urlLiftingCompleted; } + catch (RuntimeException e) + { + logger.error( + I18NUtil.getMessage("patch.convertContentUrls.error", e.getMessage()), + e); + return I18NUtil.getMessage("patch.convertContentUrls.error", e.getMessage()); + } finally { jobLockService.releaseLock(lockToken, LOCK); @@ -606,14 +624,20 @@ public class ContentUrlConverterPatch extends AbstractPatch return; } currentSize.setValue(currentSize.longValue() + reader.getSize()); + + // Create a savepoint + String savepointName = new Long(System.nanoTime()).toString(); + Savepoint savepoint = controlDAO.createSavepoint(savepointName); try { contentDataDAO.createContentUrlOrphaned(contentUrl); + controlDAO.releaseSavepoint(savepoint); count.setValue(count.intValue()+1); } catch (DataIntegrityViolationException e) { // That's OK, the URL was already managed + controlDAO.rollbackToSavepoint(savepoint); } allCount++; if (allCount % batchSize == 0) diff --git a/source/java/org/alfresco/repo/domain/control/AbstractControlDAOImpl.java b/source/java/org/alfresco/repo/domain/control/AbstractControlDAOImpl.java new file mode 100644 index 0000000000..70401fc3b3 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/control/AbstractControlDAOImpl.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain.control; + +import java.sql.Savepoint; + +/** + * Abstract implementation for connection controlling DAO. + *

+ * Provides any basic logic. + * + * @author Derek Hulley + * @since 3.2SP1 + */ +public abstract class AbstractControlDAOImpl implements ControlDAO +{ + /** + * @return Returns null by default i.e. not supported + */ + public Savepoint createSavepoint(String savepoint) + { + return null; + } + + /** No-op */ + public void rollbackToSavepoint(Savepoint savepoint) + { + } + + /** No-op */ + public void releaseSavepoint(Savepoint savepoint) + { + } +} diff --git a/source/java/org/alfresco/repo/domain/control/ControlDAO.java b/source/java/org/alfresco/repo/domain/control/ControlDAO.java new file mode 100644 index 0000000000..f60387bb8b --- /dev/null +++ b/source/java/org/alfresco/repo/domain/control/ControlDAO.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain.control; + +import java.sql.Savepoint; + +/** + * DAO services for database control statements. It is sometimes necessary to + * issue control statements on a database connection; these are not usually + * supported in the ANSI SQL standard. + * + * @author Derek Hulley + * @since 3.2SP1 + */ +public interface ControlDAO +{ + /** + * Begin batching prepared statements for later execution. + * + * @see #executeBatch() + */ + public void startBatch(); + + /** + * Execute statements that were queued for batching. + * + * @see #startBatch() + */ + public void executeBatch(); + + /** + * Create a "Save Point" in the current transaction, for later selective rollback. + * Creation should be accompanied by a matching {@link #rollbackToSavepoint(String)} + * or {@link #releaseSavepoint(String)} using the same name. + * + * @param savepoint the name of the save point + * @return Returns the handle to the savepoint or null if the + * implementation does not support it + */ + public Savepoint createSavepoint(String savepoint); + + /** + * Roll back to a previously-created "Save Point", discarding any intervening + * changes to the current transaction. + * + * @param savepoint a previously-created savepoint + * + * @see #createSavepoint(String) + */ + public void rollbackToSavepoint(Savepoint savepoint); + + /** + * Remove a previously-created "Save Point", writing any intervening updates + * into the current transaction. + * + * @param savepoint the name of the save point + * + * @see #createSavepoint(String) + */ + public void releaseSavepoint(Savepoint savepoint); +} diff --git a/source/java/org/alfresco/repo/domain/control/ibatis/ControlDAOImpl.java b/source/java/org/alfresco/repo/domain/control/ibatis/ControlDAOImpl.java new file mode 100644 index 0000000000..f22be817e9 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/control/ibatis/ControlDAOImpl.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain.control.ibatis; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Savepoint; + +import org.alfresco.repo.domain.control.AbstractControlDAOImpl; +import org.springframework.jdbc.datasource.DataSourceUtils; +import org.springframework.orm.ibatis.SqlMapClientTemplate; + +import com.ibatis.sqlmap.client.SqlMapClient; + +/** + * iBatis-specific, DB-agnostic implementation for connection controlling DAO. + * + * @author Derek Hulley + * @since 3.2SP1 + */ +public class ControlDAOImpl extends AbstractControlDAOImpl +{ + /** + * The iBatis-specific template for convenient statement execution. + */ + protected SqlMapClientTemplate template; + + public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate) + { + this.template = sqlMapClientTemplate; + } + + public void startBatch() + { + /* + * The 'transactions' here are just iBatis internal markers and + * don't have any effect other than to let iBatis know that a batch + * is possible. + */ + SqlMapClient sqlMapClient = template.getSqlMapClient(); + try + { + sqlMapClient.startTransaction(); + sqlMapClient.startBatch(); + } + catch (SQLException e) + { + throw new RuntimeException("Failed to start DAO batch.", e); + } + } + + public void executeBatch() + { + /* + * The 'transactions' here are just iBatis internal markers and + * don't have any effect other than to let iBatis know that a batch + * is possible. + */ + SqlMapClient sqlMapClient = template.getSqlMapClient(); + try + { + sqlMapClient.executeBatch(); + sqlMapClient.commitTransaction(); + sqlMapClient.endTransaction(); + } + catch (SQLException e) + { + throw new RuntimeException("Failed to start DAO batch.", e); + } + } + + /** + * PostgreSQL-specific implementation for control DAO. + * + * @author Derek Hulley + * @since 3.2SP1 + */ + public static class PostgreSQL extends ControlDAOImpl + { + /** + * Calls through to the {@link Connection#setSavepoint(String) current connection}. + */ + @Override + public Savepoint createSavepoint(final String savepoint) + { + try + { + Connection connection = DataSourceUtils.getConnection(template.getDataSource()); + return connection.setSavepoint(savepoint); + } + catch (SQLException e) + { + throw new RuntimeException("Failed to create SAVEPOINT: " + savepoint, e); + } + } + /** + * Calls through to the {@link Connection#setSavepoint(String) current connection}. + */ + @Override + public void rollbackToSavepoint(Savepoint savepoint) + { + try + { + Connection connection = DataSourceUtils.getConnection(template.getDataSource()); + connection.rollback(savepoint); + } + catch (SQLException e) + { + throw new RuntimeException("Failed to create SAVEPOINT: " + savepoint, e); + } + } + @Override + public void releaseSavepoint(Savepoint savepoint) + { + try + { + Connection connection = DataSourceUtils.getConnection(template.getDataSource()); + connection.releaseSavepoint(savepoint); + } + catch (SQLException e) + { + throw new RuntimeException("Failed to create SAVEPOINT: " + savepoint, e); + } + } + } +}