diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml index 6fa9cc4e44..74e81924ab 100644 --- a/config/alfresco/dao/dao-context.xml +++ b/config/alfresco/dao/dao-context.xml @@ -145,6 +145,9 @@ + + + diff --git a/source/java/org/alfresco/repo/domain/hibernate/dialect/AlfrescoMySQLClusterNDBDialect.java b/source/java/org/alfresco/repo/domain/hibernate/dialect/AlfrescoMySQLClusterNDBDialect.java new file mode 100644 index 0000000000..74e714a243 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/dialect/AlfrescoMySQLClusterNDBDialect.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2016 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.repo.domain.hibernate.dialect; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.dialect.MySQLInnoDBDialect; + +/** + * MySQL Cluster NDB specific DAO overrides + * + * WARNING: + * - Experimental only (unsupported) ! + * - The NDB storage engine is *not* currently supported or certified ! + * - Can be used for dev/test evaluation (please give us feedback) + * - Should not be used for live/prod env with real data ! + * - Requires FK support (hence NDB 7.3.x or higher) + * + * @author janv + * + */ +//note: *not* a dialect of InnoDB but, for now, extends here so that we can override those scripts +public class AlfrescoMySQLClusterNDBDialect extends MySQLInnoDBDialect +{ + protected Log logger = LogFactory.getLog(AlfrescoMySQLClusterNDBDialect.class); + + public AlfrescoMySQLClusterNDBDialect() + { + super(); + + logger.error("Using NDB with Alfresco is experimental and unsupported (do not use for live/prod envs) !"); + } + + public String getTableTypeString() { + return " engine=NDB"; + } + +} diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index b6524205d1..11ff333f77 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2013 Alfresco Software Limited. + * Copyright (C) 2005-2016 Alfresco Software Limited. * * This file is part of Alfresco * @@ -1390,6 +1390,37 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO if (!allowAuditableAspect) addAuditableAspect = false; + Long id = newNodeImplInsert(node); + node.setId(id); + + Set nodeAspects = null; + if (addAuditableAspect) + { + Long auditableAspectQNameId = qnameDAO.getOrCreateQName(ContentModel.ASPECT_AUDITABLE).getFirst(); + insertNodeAspect(id, auditableAspectQNameId); + nodeAspects = Collections.singleton(ContentModel.ASPECT_AUDITABLE); + } + else + { + nodeAspects = Collections.emptySet(); + } + + // Lock the node and cache + node.lock(); + nodesCache.setValue(id, node); + // Pre-populate some of the other caches so that we don't immediately query + setNodeAspectsCached(id, nodeAspects); + setNodePropertiesCached(id, Collections.emptyMap()); + + if (isDebugEnabled) + { + logger.debug("Created new node: \n" + " " + node); + } + return node; + } + + protected Long newNodeImplInsert(NodeEntity node) + { Long id = null; Savepoint savepoint = controlDAO.createSavepoint("newNodeImpl"); try @@ -1425,32 +1456,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO throw new NodeExistsException(dbTargetNode.getNodePair(), e); } } - node.setId(id); - Set nodeAspects = null; - if (addAuditableAspect) - { - Long auditableAspectQNameId = qnameDAO.getOrCreateQName(ContentModel.ASPECT_AUDITABLE).getFirst(); - insertNodeAspect(id, auditableAspectQNameId); - nodeAspects = Collections.singleton(ContentModel.ASPECT_AUDITABLE); - } - else - { - nodeAspects = Collections.emptySet(); - } - - // Lock the node and cache - node.lock(); - nodesCache.setValue(id, node); - // Pre-populate some of the other caches so that we don't immediately query - setNodeAspectsCached(id, nodeAspects); - setNodePropertiesCached(id, Collections.emptyMap()); - - if (isDebugEnabled) - { - logger.debug("Created new node: \n" + " " + node); - } - return node; + return id; } @Override diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index 5b7fa1f121..14ef4fb2b9 100644 --- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2016 Alfresco Software Limited. * * This file is part of Alfresco * @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Set; import java.util.SortedSet; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.ibatis.IdsEntity; import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.node.AbstractNodeDAOImpl; @@ -37,6 +38,7 @@ import org.alfresco.repo.domain.node.Node; import org.alfresco.repo.domain.node.NodeAspectsEntity; import org.alfresco.repo.domain.node.NodeAssocEntity; import org.alfresco.repo.domain.node.NodeEntity; +import org.alfresco.repo.domain.node.NodeExistsException; import org.alfresco.repo.domain.node.NodeIdAndAclId; import org.alfresco.repo.domain.node.NodePropertyEntity; import org.alfresco.repo.domain.node.NodePropertyKey; @@ -1719,7 +1721,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl } /** - * MySQL-specific DAO overrides + * MySQL (InnoDB) specific DAO overrides */ public static class MySQL extends NodeDAOImpl { @@ -1743,6 +1745,58 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl return numDeleted; } } + + /** + * MySQL Cluster NDB specific DAO overrides + * + * WARNING: Experimental/unsupported - see AlfrescoMySQLClusterNDBDialect ! + * + * @author janv + */ + public static class MySQLClusterNDB extends MySQL + { + @Override + protected Long newNodeImplInsert(NodeEntity node) + { + Long id = null; + try + { + // We need to handle existing deleted nodes. + NodeRef targetNodeRef = node.getNodeRef(); + Node dbTargetNode = selectNodeByNodeRef(targetNodeRef); + if (dbTargetNode != null) + { + if (dbTargetNode.getDeleted(qnameDAO)) + { + Long dbTargetNodeId = dbTargetNode.getId(); + // This is OK. It happens when we create a node that existed in the past. + // Remove the row completely + deleteNodeProperties(dbTargetNodeId, (Set) null); + deleteNodeById(dbTargetNodeId); + } + else + { + // A live node exists. + throw new NodeExistsException(dbTargetNode.getNodePair(), null); + } + } + + id = insertNode(node); + } + catch (Throwable e) + { + if (e instanceof NodeExistsException) + { + throw e; + } + + // There does not appear to be any row that could prevent an insert + throw new AlfrescoRuntimeException("Failed to insert new node: " + node, e); + } + + return id; + } + } /** * Get most recent transaction made in a given commit time range diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index fca4415545..de477ee0c3 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -1,6 +1,5 @@ /* - * Copyright (C) 2005-2015 Alfresco Software Limited. - + * Copyright (C) 2005-2016 Alfresco Software Limited. * * This file is part of Alfresco * @@ -70,6 +69,7 @@ import org.alfresco.repo.admin.patch.AppliedPatch; import org.alfresco.repo.admin.patch.Patch; import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch; import org.alfresco.repo.content.filestore.FileContentWriter; +import org.alfresco.repo.domain.hibernate.dialect.AlfrescoMySQLClusterNDBDialect; import org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect; import org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect; import org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect; @@ -1478,6 +1478,23 @@ public class SchemaBootstrap extends AbstractLifecycleBean sql = sql.replaceAll("(?i)TYPE=InnoDB", "ENGINE=InnoDB"); } + if (this.dialect != null && this.dialect instanceof AlfrescoMySQLClusterNDBDialect) + { + // note: enable bootstrap on MySQL Cluster NDB + /* + * WARNING: Experimental/unsupported - see AlfrescoMySQLClusterNDBDialect ! + */ + sql = sql.replaceAll("(?i)TYPE=InnoDB", "ENGINE=NDB"); // belts-and-braces + sql = sql.replaceAll("(?i)ENGINE=InnoDB", "ENGINE=NDB"); + + sql = sql.replaceAll("(?i) BIT ", " BOOLEAN "); + sql = sql.replaceAll("(?i) BIT,", " BOOLEAN,"); + + sql = sql.replaceAll("(?i) string_value text", " string_value VARCHAR(1024)"); + + sql = sql.replaceAll("(?i) VARCHAR(4000)", "TEXT(4000)"); + } + Object fetchedVal = executeStatement(connection, sql, fetchColumnName, optional, line, scriptFile); if (fetchVarName != null && fetchColumnName != null) { @@ -1635,6 +1652,12 @@ public class SchemaBootstrap extends AbstractLifecycleBean // serializable_value blob, maxStringLength = Integer.MAX_VALUE; } + else if (dialect instanceof AlfrescoMySQLClusterNDBDialect) + { + // string_value varchar(1024), + // serializable_value blob, + maxStringLength = SchemaBootstrap.DEFAULT_MAX_STRING_LENGTH; + } else if (dialect instanceof AlfrescoOracle9Dialect) { // string_value varchar2(1024 char), diff --git a/source/java/org/alfresco/repo/domain/schema/script/ScriptExecutorImpl.java b/source/java/org/alfresco/repo/domain/schema/script/ScriptExecutorImpl.java index 3996fbde8b..190f74aeb8 100644 --- a/source/java/org/alfresco/repo/domain/schema/script/ScriptExecutorImpl.java +++ b/source/java/org/alfresco/repo/domain/schema/script/ScriptExecutorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2014 Alfresco Software Limited. + * Copyright (C) 2005-2016 Alfresco Software Limited. * * This file is part of Alfresco * @@ -35,6 +35,7 @@ import javax.sql.DataSource; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.content.filestore.FileContentWriter; +import org.alfresco.repo.domain.hibernate.dialect.AlfrescoMySQLClusterNDBDialect; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.util.LogUtil; import org.alfresco.util.TempFileProvider; @@ -537,6 +538,23 @@ public class ScriptExecutorImpl implements ScriptExecutor sql = sql.replaceAll("(?i)TYPE=InnoDB", "ENGINE=InnoDB"); } + if (this.dialect != null && this.dialect instanceof AlfrescoMySQLClusterNDBDialect) + { + // note: enable bootstrap on MySQL Cluster NDB + /* + * WARNING: Experimental/unsupported - see AlfrescoMySQLClusterNDBDialect ! + */ + sql = sql.replaceAll("(?i)TYPE=InnoDB", "ENGINE=NDB"); // belts-and-braces + sql = sql.replaceAll("(?i)ENGINE=InnoDB", "ENGINE=NDB"); + + sql = sql.replaceAll("(?i) BIT ", " BOOLEAN "); + sql = sql.replaceAll("(?i) BIT,", " BOOLEAN,"); + + sql = sql.replaceAll("(?i) string_value text", " string_value VARCHAR(1024)"); + + sql = sql.replaceAll("(?i) VARCHAR(4000)", "TEXT(4000)"); + } + Object fetchedVal = executeStatement(connection, sql, fetchColumnName, optional, line, scriptFile); if (fetchVarName != null && fetchColumnName != null) { diff --git a/source/test-java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java b/source/test-java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java index df36e050a5..57d3fd7d16 100644 --- a/source/test-java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java +++ b/source/test-java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2014 Alfresco Software Limited. + * Copyright (C) 2005-2016 Alfresco Software Limited. * * This file is part of Alfresco * @@ -916,6 +916,8 @@ public class ArchiveAndRestoreTest extends TestCase RestoreNodeReport report = nodeArchiveService.restoreArchivedNode(r_); assertEquals("Restore failed", RestoreStatus.SUCCESS, report.getStatus()); + commitAndBeginNewTransaction(); + //It is restored, still with no AUDITABLE ASPECT verifyNodeExistence(r, true); verifyAspectExistence(r, ContentModel.ASPECT_AUDITABLE, false);