diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 4d2f7be45d..8bf5a0e273 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -54,12 +54,19 @@ ${db.schema.update} + + ${db.schema.stopAfterSchemaBootstrap} + ${db.schema.update.lockRetryCount} ${db.schema.update.lockRetryWaitSeconds} + + + + classpath:alfresco/dbscripts/create/2.2/${db.script.dialect}/AlfrescoPostCreate-2.2-MappedFKIndexes.sql diff --git a/config/alfresco/messages/schema-update.properties b/config/alfresco/messages/schema-update.properties index fdc3ea2c93..0521703f2e 100644 --- a/config/alfresco/messages/schema-update.properties +++ b/config/alfresco/messages/schema-update.properties @@ -2,7 +2,10 @@ schema.update.msg.dialect_used=Schema managed by database dialect {0}. schema.update.msg.bypassing=Bypassing schema update checks. -schema.update.msg.all_statements=All executed statements written to file {0}. +schema.update.msg.normalized_schema=Normalized schema dumped to file {0}. +schema.update.msg.normalized_schema_pre=Normalized schema (pre-bootstrap) dumped to file {0}. +schema.update.msg.normalized_schema_post=Normalized schema (post-bootstrap) dumped to file {0}. +schema.update.msg.all_statements=All executed statements: {0}. schema.update.msg.no_changes=No changes were made to the schema. schema.update.msg.executing_generated_script=Executing database script {0} (Generated). schema.update.msg.executing_copied_script=Executing database script {0} (Copied from {1}). @@ -12,6 +15,7 @@ schema.update.warn.dialect_unsupported=Alfresco should not be used with database schema.update.warn.dialect_hsql=Alfresco is using the HSQL default database. Please only use this while evaluating Alfresco, it is NOT recommended for production or deployment! schema.update.warn.dialect_derby=Alfresco is using the Apache Derby default database. Please only use this while evaluating Alfresco, it is NOT recommended for production or deployment! schema.update.warn.dialect_substituting=The dialect ''{0}'' is being changed to ''{1}''. +schema.update.err.forced_stop=The property 'stopAfterSchemaBootstrap' has been set. The bootstrap process is being terminated. schema.update.err.dialect_should_use=The dialect ''{0}'' is unsupported. Please use ''{1}'' instead. schema.update.err.found_multiple=\nMore than one Alfresco schema was found when querying the database metadata.\n Limit the database user's permissions or set the 'hibernate.default_schema' property in 'custom-hibernate-dialect.properties'. schema.update.err.previous_failed=A previous schema upgrade failed or was not completed. Revert to the original database before attempting the upgrade again. diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 9a31ca7df1..74c4333916 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -79,6 +79,9 @@ + + + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index fc893ba586..c1ee40b806 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -171,6 +171,7 @@ lucene.commit.lock.timeout=100000 lucene.lock.poll.interval=100 # Database configuration +db.schema.stopAfterSchemaBootstrap=false db.schema.update=true db.schema.update.lockRetryCount=24 db.schema.update.lockRetryWaitSeconds=5 diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index c0c1d8d995..74d90968c1 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Properties; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.admin.patch.Patch; import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch; import org.alfresco.repo.content.filestore.FileContentWriter; @@ -58,6 +59,7 @@ import org.alfresco.util.AbstractLifecycleBean; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.LogUtil; import org.alfresco.util.TempFileProvider; +import org.alfresco.util.schemadump.Main; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; @@ -101,6 +103,9 @@ public class SchemaBootstrap extends AbstractLifecycleBean private static final String MSG_DIALECT_USED = "schema.update.msg.dialect_used"; private static final String MSG_BYPASSING_SCHEMA_UPDATE = "schema.update.msg.bypassing"; + private static final String MSG_NORMALIZED_SCHEMA = "schema.update.msg.normalized_schema"; + private static final String MSG_NORMALIZED_SCHEMA_PRE = "schema.update.msg.normalized_schema_pre"; + private static final String MSG_NORMALIZED_SCHEMA_POST = "schema.update.msg.normalized_schema_post"; private static final String MSG_NO_CHANGES = "schema.update.msg.no_changes"; private static final String MSG_ALL_STATEMENTS = "schema.update.msg.all_statements"; private static final String MSG_EXECUTING_GENERATED_SCRIPT = "schema.update.msg.executing_generated_script"; @@ -111,6 +116,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean private static final String WARN_DIALECT_SUBSTITUTING = "schema.update.warn.dialect_substituting"; private static final String WARN_DIALECT_HSQL = "schema.update.warn.dialect_hsql"; private static final String WARN_DIALECT_DERBY = "schema.update.warn.dialect_derby"; + private static final String ERR_FORCED_STOP = "schema.update.err.forced_stop"; private static final String ERR_DIALECT_SHOULD_USE = "schema.update.err.dialect_should_use"; private static final String ERR_MULTIPLE_SCHEMAS = "schema.update.err.found_multiple"; private static final String ERR_PREVIOUS_FAILED_BOOTSTRAP = "schema.update.err.previous_failed"; @@ -156,6 +162,8 @@ public class SchemaBootstrap extends AbstractLifecycleBean private LocalSessionFactoryBean localSessionFactory; private String schemaOuputFilename; private boolean updateSchema; + private boolean stopAfterSchemaBootstrap; + private List preCreateScriptUrls; private List postCreateScriptUrls; private List validateUpdateScriptPatches; private List preUpdateScriptPatches; @@ -168,6 +176,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean public SchemaBootstrap() { + preCreateScriptUrls = new ArrayList(1); postCreateScriptUrls = new ArrayList(1); validateUpdateScriptPatches = new ArrayList(4); preUpdateScriptPatches = new ArrayList(4); @@ -207,7 +216,34 @@ public class SchemaBootstrap extends AbstractLifecycleBean } /** - * Set the scripts that must be executed after the schema has been created. + * Set whether this component should terminate the bootstrap process after running all the + * usual checks and scripts. This has the additional effect of dumping a final schema + * structure file just before exiting. + *

+ * WARNING: USE FOR DEBUG AND UPGRADE TESTING ONLY + * + * @param stopAfterSchemaBootstrap true to terminate (with exception) after + * running all the usual schema updates and checks. + */ + public void setStopAfterSchemaBootstrap(boolean stopAfterSchemaBootstrap) + { + this.stopAfterSchemaBootstrap = stopAfterSchemaBootstrap; + } + + /** + * Set the scripts that must be executed before the schema has been created. + * + * @param postCreateScriptUrls file URLs + * + * @see #PLACEHOLDER_SCRIPT_DIALECT + */ + public void setPreCreateScriptUrls(List preUpdateScriptUrls) + { + this.preCreateScriptUrls = preUpdateScriptUrls; + } + + /** + * Set the scripts that must be executed after the schema has been created. * * @param postCreateScriptUrls file URLs * @@ -345,6 +381,23 @@ public class SchemaBootstrap extends AbstractLifecycleBean { private static final long serialVersionUID = 5574280159910824660L; } + + /** + * Used to indicate a forced stop of the bootstrap. + * + * @see SchemaBootstrap#setStopAfterSchemaBootstrap(boolean) + * + * @author Derek Hulley + * @since 3.1.1 + */ + private static class BootstrapStopException extends RuntimeException + { + private static final long serialVersionUID = 4250016675538442181L; + private BootstrapStopException() + { + super(I18NUtil.getMessage(ERR_FORCED_STOP)); + } + } /** * Count applied patches. This fails if multiple applied patch tables are found, @@ -618,13 +671,18 @@ public class SchemaBootstrap extends AbstractLifecycleBean } // Get the dialect final Dialect dialect = Dialect.getDialect(cfg.getProperties()); - String dialectStr = dialect.getClass().getName(); + String dialectStr = dialect.getClass().getSimpleName(); if (create) { + // execute pre-create scripts (not patches) + for (String scriptUrl : this.preCreateScriptUrls) + { + executeScriptUrl(cfg, connection, scriptUrl); + } // the applied patch table is missing - we assume that all other tables are missing // perform a full update using Hibernate-generated statements - File tempFile = TempFileProvider.createTempFile("AlfrescoSchemaCreate-" + dialectStr + "-", ".sql"); + File tempFile = TempFileProvider.createTempFile("AlfrescoSchema-" + dialectStr + "-Update-", ".sql"); SchemaBootstrap.dumpSchemaCreate(cfg, tempFile); executeScriptFile(cfg, connection, tempFile, null); // execute post-create scripts (not patches) @@ -649,7 +707,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean String[] sqls = cfg.generateSchemaUpdateScript(dialect, metadata); if (sqls.length > 0) { - tempFile = TempFileProvider.createTempFile("AlfrescoSchemaUpdate-" + dialectStr + "-", ".sql"); + tempFile = TempFileProvider.createTempFile("AlfrescoSchema-" + dialectStr + "-Update-", ".sql"); writer = new BufferedWriter(new FileWriter(tempFile)); for (String sql : sqls) { @@ -743,7 +801,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean private void executeScriptUrl(Configuration cfg, Connection connection, String scriptUrl) throws Exception { Dialect dialect = Dialect.getDialect(cfg.getProperties()); - String dialectStr = dialect.getClass().getName(); + String dialectStr = dialect.getClass().getSimpleName(); InputStream scriptInputStream = getScriptInputStream(dialect.getClass(), scriptUrl); // check that it exists if (scriptInputStream == null) @@ -754,7 +812,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean File tempFile = null; try { - tempFile = TempFileProvider.createTempFile("AlfrescoSchemaUpdate-" + dialectStr + "-", ".sql"); + tempFile = TempFileProvider.createTempFile("AlfrescoSchema-" + dialectStr + "-Update-", ".sql"); ContentWriter writer = new FileContentWriter(tempFile); writer.putContent(scriptInputStream); } @@ -1115,6 +1173,15 @@ public class SchemaBootstrap extends AbstractLifecycleBean // Update the schema, if required. if (updateSchema) { + // Dump the normalized, pre-upgrade Alfresco schema. We keep the file for later reporting. + File xmlPreSchemaOutputFile = dumpSchema( + connection, + dialect, + TempFileProvider.createTempFile( + "AlfrescoSchema-" + dialect.getClass().getSimpleName() + "-", + "-Startup.xml").getPath(), + "Failed to dump normalized, pre-upgrade schema to file."); + // Retries are required here as the DB lock will be applied lazily upon first statement execution. // So if the schema is up to date (no statements executed) then the LockFailException cannot be // thrown. If it is thrown, the the update needs to be rerun as it will probably generate no SQL @@ -1150,8 +1217,11 @@ public class SchemaBootstrap extends AbstractLifecycleBean } else { - schemaOutputFile = TempFileProvider.createTempFile("AlfrescoSchemaUpdate-All_Statements-", ".sql"); + schemaOutputFile = TempFileProvider.createTempFile( + "AlfrescoSchema-" + dialect.getClass().getSimpleName() + "-All_Statements-", + ".sql"); } + StringBuilder executedStatements = executedStatementsThreadLocal.get(); if (executedStatements == null) { @@ -1179,11 +1249,61 @@ public class SchemaBootstrap extends AbstractLifecycleBean // Remove the flag indicating a running bootstrap setBootstrapCompleted(connection); } + + // Dump the normalized, post-upgrade Alfresco schema. + File xmlPostSchemaOutputFile = dumpSchema( + connection, + dialect, + TempFileProvider.createTempFile( + "AlfrescoSchema-" + dialect.getClass().getSimpleName() + "-", + ".xml").getPath(), + "Failed to dump normalized, post-upgrade schema to file."); + + // Report normalized dumps + if (createdSchema) + { + // This is a new schema + if (xmlPostSchemaOutputFile != null) + { + LogUtil.info(logger, MSG_NORMALIZED_SCHEMA, xmlPostSchemaOutputFile.getPath()); + } + } + else if (executedStatements != null) + { + // We upgraded, so have to report pre- and post- schema dumps + if (xmlPreSchemaOutputFile != null) + { + LogUtil.info(logger, MSG_NORMALIZED_SCHEMA_PRE, xmlPreSchemaOutputFile.getPath()); + } + if (xmlPostSchemaOutputFile != null) + { + LogUtil.info(logger, MSG_NORMALIZED_SCHEMA_POST, xmlPostSchemaOutputFile.getPath()); + } + } } else { LogUtil.info(logger, MSG_BYPASSING_SCHEMA_UPDATE); } + + if (stopAfterSchemaBootstrap) + { + // We have been forced to stop, so we do one last dump of the schema and throw an exception to + // escape further startup procedures + File xmlStopSchemaOutputFile = dumpSchema( + connection, + dialect, + TempFileProvider.createTempFile( + "AlfrescoSchema-" + dialect.getClass().getSimpleName() + "-", + "-ForcedExit.xml").getPath(), + "Failed to dump normalized, post-upgrade, forced-exit schema to file."); + if (xmlStopSchemaOutputFile != null) + { + LogUtil.info(logger, MSG_NORMALIZED_SCHEMA, xmlStopSchemaOutputFile); + } + LogUtil.error(logger, ERR_FORCED_STOP); + throw new BootstrapStopException(); + } // Reset the configuration cfg.setProperty(Environment.CONNECTION_PROVIDER, defaultConnectionProviderFactoryClass); @@ -1191,6 +1311,11 @@ public class SchemaBootstrap extends AbstractLifecycleBean // all done successfully ((ApplicationContext) event.getSource()).publishEvent(new SchemaAvailableEvent(this)); } + catch (BootstrapStopException e) + { + // We just let this out + throw e; + } catch (Throwable e) { LogUtil.error(logger, e, ERR_UPDATE_FAILED); @@ -1221,6 +1346,33 @@ public class SchemaBootstrap extends AbstractLifecycleBean } } + + /** + * @return Returns the file that was written to or null if it failed + */ + private File dumpSchema(Connection connection, Dialect dialect, String fileName, String err) + { + File xmlSchemaOutputFile = new File(fileName); + try + { + Main xmlSchemaOutputMain = new Main(connection, dialect); + xmlSchemaOutputMain.execute(xmlSchemaOutputFile); + } + catch (Throwable e) + { + xmlSchemaOutputFile = null; + // Don't fail the upgrade on this + if (logger.isDebugEnabled()) + { + logger.debug(err, e); + } + else + { + logger.error(err + " Error: " + e.getMessage()); + } + } + return xmlSchemaOutputFile; + } @Override protected void onShutdown(ApplicationEvent event) diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 332ea30736..0bc5abad58 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -544,6 +544,8 @@ public class FileFolderServiceImpl implements FileFolderService newName = beforeFileInfo.getName(); } + boolean nameChanged = (newName.equals(beforeFileInfo.getName()) == false); + // we need the current association type ChildAssociationRef assocRef = nodeService.getPrimaryParent(sourceNodeRef); if (targetParentRef == null) @@ -570,9 +572,21 @@ public class FileFolderServiceImpl implements FileFolderService } } - QName qname = QName.createQName( - NamespaceService.CONTENT_MODEL_1_0_URI, - QName.createValidLocalName(newName)); + QName qname; + if (nameChanged) + { + // Change the localname to match the new name + qname = QName.createQName( + assocRef.getQName().getNamespaceURI(), + QName.createValidLocalName(newName)); + } + else + { + // Keep the localname + qname = QName.createQName( + assocRef.getQName().getNamespaceURI(), + assocRef.getQName().getLocalName()); + } QName targetParentType = nodeService.getType(targetParentRef); diff --git a/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java b/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java index f9e8e1a791..3f3dcf2c2e 100644 --- a/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/EditionServiceImpl.java @@ -46,6 +46,7 @@ import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionService; @@ -222,6 +223,11 @@ public class EditionServiceImpl implements EditionService public Map getVersionedMetadatas(Version version) { NodeRef frozenNodeRef = version.getFrozenStateNodeRef(); + if (frozenNodeRef.getStoreRef().getIdentifier().equals("lightWeightVersionStore")) + { + // The data stored belonged to the old version store + Map versionProps = version.getVersionProperties(); + } if(ContentModel.TYPE_MULTILINGUAL_CONTAINER.equals(nodeService.getType(frozenNodeRef))) { diff --git a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java index 6689558ec3..354c5cea38 100644 --- a/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java +++ b/source/java/org/alfresco/repo/model/ml/MultilingualContentServiceImpl.java @@ -51,6 +51,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; @@ -89,6 +90,7 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic private PermissionService permissionService; private ContentFilterLanguagesService contentFilterLanguagesService; private FileFolderService fileFolderService; + private VersionService versionService; private BehaviourFilter policyBehaviourFilter; @@ -259,6 +261,11 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic private NodeRef makeTranslationImpl(NodeRef mlContainerNodeRef, NodeRef contentNodeRef, Locale locale) { + // Previous versions of the document are not compatible with the versioning requirements + // dictated by the aspects about to be added. A version has to be forced if the aspect + // already exists. + // https://issues.alfresco.com/jira/browse/ETHREEOH-1657 + boolean forceNewVersion = nodeService.hasAspect(contentNodeRef, ContentModel.ASPECT_VERSIONABLE); // Add the aspect using the given locale, of necessary if (!nodeService.hasAspect(contentNodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT)) { @@ -272,6 +279,11 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic // The aspect is present, so just ensure that the locale is correct nodeService.setProperty(contentNodeRef, ContentModel.PROP_LOCALE, locale); } + + if (forceNewVersion) + { + versionService.createVersion(contentNodeRef, null); + } // Do we make use of an existing container? if (mlContainerNodeRef == null) @@ -963,6 +975,11 @@ public class MultilingualContentServiceImpl implements MultilingualContentServic this.fileFolderService = fileFolderService; } + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + public void setPolicyBehaviourFilter(BehaviourFilter policyBehaviourFilter) { this.policyBehaviourFilter = policyBehaviourFilter; diff --git a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java index ce59ebe6d8..501722b025 100644 --- a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java +++ b/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Alfresco Software Limited. + * 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 @@ -28,6 +28,7 @@ import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -122,7 +123,6 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest * Test user details */ private static final String PWD = "admin"; -// private static final String USER_NAME = "admin"; /** * Sets the meta model dao @@ -254,6 +254,11 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest ContentWriter contentWriter = this.contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); contentWriter.putContent(TEST_CONTENT); + // Set author + Map authorProps = new HashMap(1, 1.0f); + authorProps.put(ContentModel.PROP_AUTHOR, "Charles Dickens"); + this.dbNodeService.addAspect(nodeRef, ContentModel.ASPECT_AUTHOR, authorProps); + // Add some children to the node NodeRef child1 = this.dbNodeService.createNode( nodeRef, @@ -323,8 +328,8 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest int nextVersion = peekNextVersionNumber(); String nextVersionLabel = peekNextVersionLabel(versionableNode, nextVersion, versionProperties); - // Snap-shot the date-time - long beforeVersionTime = System.currentTimeMillis(); + // Snap-shot the node created date-time + long beforeVersionTime = ((Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED)).getTime(); // Now lets create a new version for this node Version newVersion = versionService.createVersion(versionableNode, this.versionProperties); @@ -340,8 +345,8 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest int nextVersion = peekNextVersionNumber(); String nextVersionLabel = peekNextVersionLabel(versionableNode, nextVersion, versionProperties); - // Snap-shot the date-time - long beforeVersionTime = System.currentTimeMillis(); + // Snap-shot the node created date-time + long beforeVersionTime = ((Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED)).getTime(); // Now lets create new version for this node (optionally with children) Collection versions = versionService.createVersion(versionableNode, this.versionProperties, versionChildren); diff --git a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java index cc948cc05d..f8c96ba01b 100644 --- a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java +++ b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java @@ -110,9 +110,6 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe return new StoreRef(StoreRef.PROTOCOL_WORKSPACE, Version2Model.STORE_ID); } - /* (non-Javadoc) - * @see org.alfresco.repo.service.cmr.version.VersionService#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map) - */ public Version createVersion( NodeRef nodeRef, Map versionProperties) @@ -343,14 +340,11 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe VersionHistory versionHistory = null; - // get version history, if the 'live' (versioned) node exists - if (this.nodeService.exists(nodeRef) == true) + // Get the version history regardless of whether the node is still 'live' or not + NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); + if (versionHistoryRef != null) { - NodeRef versionHistoryRef = getVersionHistoryNodeRef(nodeRef); - if (versionHistoryRef != null) - { - versionHistory = buildVersionHistory(versionHistoryRef, nodeRef); - } + versionHistory = buildVersionHistory(versionHistoryRef, nodeRef); } return versionHistory; diff --git a/source/java/org/alfresco/repo/version/VersionMigratorTest.java b/source/java/org/alfresco/repo/version/VersionMigratorTest.java index 51d9d210bf..484b6200de 100644 --- a/source/java/org/alfresco/repo/version/VersionMigratorTest.java +++ b/source/java/org/alfresco/repo/version/VersionMigratorTest.java @@ -111,8 +111,8 @@ public class VersionMigratorTest extends BaseVersionStoreTest int nextVersion = peekNextVersionNumber(); String nextVersionLabel = peekNextVersionLabel(versionableNode, nextVersion, versionProperties); - // Snap-shot the date-time - Date beforeVersionDate = new Date(); + // Snap-shot the node created date-time + Date beforeVersionDate = (Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED); long beforeVersionTime = beforeVersionDate.getTime(); logger.info("beforeVersion Date/Time: " + beforeVersionDate + " [" + beforeVersionTime + "]"); @@ -205,7 +205,9 @@ public class VersionMigratorTest extends BaseVersionStoreTest // Get the next version number, next version label and snapshot the date-time int nextVersion1 = peekNextVersionNumber(); String nextVersionLabel1 = peekNextVersionLabel(versionableNode, nextVersion1, versionProperties); - long beforeVersionTime1 = System.currentTimeMillis(); + + // Snap-shot the node created date-time + long beforeVersionTime1 = ((Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED)).getTime(); Version version1 = createVersion(versionableNode); logger.info(version1); @@ -213,7 +215,9 @@ public class VersionMigratorTest extends BaseVersionStoreTest // Get the next version number, next version label and snapshot the date-time int nextVersion2 = peekNextVersionNumber(); String nextVersionLabel2 = peekNextVersionLabel(versionableNode, nextVersion2, versionProperties); - long beforeVersionTime2 = System.currentTimeMillis(); + + // Snap-shot the node created date-time + long beforeVersionTime2 = ((Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED)).getTime(); Version version2 = createVersion(versionableNode); logger.info(version2); @@ -221,7 +225,9 @@ public class VersionMigratorTest extends BaseVersionStoreTest // Get the next version number, next version label and snapshot the date-time int nextVersion3 = peekNextVersionNumber(); String nextVersionLabel3 = peekNextVersionLabel(versionableNode, nextVersion3, versionProperties); - long beforeVersionTime3 = System.currentTimeMillis(); + + // Snap-shot the node created date-time + long beforeVersionTime3 = ((Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED)).getTime(); Version version3 = createVersion(versionableNode); logger.info(version3); diff --git a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java index bf375eda54..f4979a964a 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Alfresco Software Limited. + * 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 @@ -27,6 +27,7 @@ package org.alfresco.repo.version; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -144,7 +145,9 @@ public class VersionServiceImplTest extends BaseVersionStoreTest // Snap shot data int expectedVersionNumber = peekNextVersionNumber(); String expectedVersionLabel = peekNextVersionLabel(versionableNode, expectedVersionNumber, versionProperties); - long beforeVersionTime = System.currentTimeMillis(); + + // Snap-shot the node created date-time + long beforeVersionTime = ((Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED)).getTime(); // Version the node and its children Collection versions = this.versionService.createVersion( @@ -167,8 +170,10 @@ public class VersionServiceImplTest extends BaseVersionStoreTest // Snap shot data int expectedVersionNumber = peekNextVersionNumber(); - String expectedVersionLabel = peekNextVersionLabel(versionableNode, expectedVersionNumber, versionProperties); - long beforeVersionTime = System.currentTimeMillis(); + String expectedVersionLabel = peekNextVersionLabel(versionableNode, expectedVersionNumber, versionProperties); + + // Snap-shot the node created date-time + long beforeVersionTime = ((Date)nodeService.getProperty(versionableNode, ContentModel.PROP_CREATED)).getTime(); // Version the list of nodes created Collection versions = this.versionService.createVersion( diff --git a/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java b/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java index 8158e1bd9c..3a0ccf1e60 100644 --- a/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java +++ b/source/java/org/alfresco/repo/version/common/VersionHistoryImpl.java @@ -24,7 +24,10 @@ */ package org.alfresco.repo.version.common; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; +import java.io.ObjectInputStream.GetField; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -45,9 +48,6 @@ import org.alfresco.util.VersionNumber; */ public class VersionHistoryImpl implements VersionHistory { - /* - * Serial version UID - */ private static final long serialVersionUID = 3257001051558326840L; /* @@ -56,9 +56,10 @@ public class VersionHistoryImpl implements VersionHistory private static final String ERR_MSG = "The root version must be specified when creating a version history object."; /* - * The root version label + * Field is left here to aid in detection of old serialized versions */ - private String rootVersionLabel = null; + @SuppressWarnings("unused") + private transient List versions; /* * Version history tree structure map @@ -74,7 +75,6 @@ public class VersionHistoryImpl implements VersionHistory * Versions ordered by creation date (descending) */ private static Comparator versionComparatorDesc = new VersionComparatorDesc(); - private List versions = new ArrayList(); /** * Root version @@ -101,7 +101,6 @@ public class VersionHistoryImpl implements VersionHistory this.versionsByLabel = new HashMap(); this.rootVersion = rootVersion; - this.rootVersionLabel = rootVersion.getVersionLabel(); addVersion(rootVersion, null); } @@ -125,8 +124,10 @@ public class VersionHistoryImpl implements VersionHistory */ public Collection getAllVersions() { - Collections.sort(versions, versionComparatorDesc); - return versions; + Collection versions = versionsByLabel.values(); + List sortedVersions = new ArrayList(versions); + Collections.sort(sortedVersions, versionComparatorDesc); + return sortedVersions; } /** @@ -211,7 +212,6 @@ public class VersionHistoryImpl implements VersionHistory // TODO cope with exception case where duplicate version labels have been specified this.versionsByLabel.put(version.getVersionLabel(), version); - this.versions.add(version); if (predecessor != null) { @@ -222,7 +222,7 @@ public class VersionHistoryImpl implements VersionHistory /** * Version Comparator * - * Note: Descending create date order + * Note: Descending (last modified) date order */ public static class VersionComparatorDesc implements Comparator, Serializable { @@ -230,7 +230,7 @@ public class VersionHistoryImpl implements VersionHistory public int compare(Version v1, Version v2) { - int result = v2.getCreatedDate().compareTo(v1.getCreatedDate()); + int result = v2.getFrozenModifiedDate().compareTo(v1.getFrozenModifiedDate()); if (result == 0) { result = new VersionNumber(v2.getVersionLabel()).compareTo(new VersionNumber(v1.getVersionLabel())); @@ -242,7 +242,7 @@ public class VersionHistoryImpl implements VersionHistory /** * Version Comparator * - * Note: Ascending create date order + * Note: Ascending (last modified) date order */ public static class VersionComparatorAsc implements Comparator, Serializable { @@ -250,7 +250,7 @@ public class VersionHistoryImpl implements VersionHistory public int compare(Version v1, Version v2) { - int result = v1.getCreatedDate().compareTo(v2.getCreatedDate()); + int result = v1.getFrozenModifiedDate().compareTo(v2.getFrozenModifiedDate()); if (result == 0) { result = new VersionNumber(v1.getVersionLabel()).compareTo(new VersionNumber(v2.getVersionLabel())); @@ -259,4 +259,29 @@ public class VersionHistoryImpl implements VersionHistory } } + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException + { + GetField fields = is.readFields(); + if (fields.defaulted("versionsByLabel")) + { + // This is a V2.2 class + // The old 'rootVersion' maps to the current 'rootVersion' + this.rootVersion = (Version) fields.get("rootVersion", null);; + // The old 'versions' maps to the current 'versionsByLabel' + this.versionsByLabel = (HashMap) fields.get("versions", new HashMap()); + // The old 'versionHistory' maps to the current 'versionHistory' + this.versionHistory = (HashMap) fields.get("versionHistory", new HashMap()); + } + else + { + // This is a V3.1.0 to ... class + // The old 'rootVersion' maps to the current 'rootVersion' + this.rootVersion = (Version) fields.get("rootVersion", null); + // The old 'versionsByLabel' maps to the current 'versionsByLabel' + this.versionsByLabel = (HashMap) fields.get("versionsByLabel", new HashMap()); + // The old 'versionHistory' maps to the current 'versionHistory' + this.versionHistory = (HashMap) fields.get("versionHistory", new HashMap()); + } + } } diff --git a/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java b/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java index ba5941f294..1f93b9b05d 100644 --- a/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java +++ b/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java @@ -24,6 +24,11 @@ */ package org.alfresco.repo.version.common; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collection; import java.util.Date; @@ -37,6 +42,9 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionDoesNotExistException; import org.alfresco.service.cmr.version.VersionServiceException; +import org.alfresco.util.TempFileProvider; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; /** * VersionHistoryImpl Unit Test Class @@ -208,6 +216,7 @@ public class VersionHistoryImplTest extends TestCase /** * Test getSuccessors */ + @SuppressWarnings("unchecked") public void testGetSuccessors() { VersionHistoryImpl vh = testAddVersionImpl(); @@ -260,4 +269,75 @@ public class VersionHistoryImplTest extends TestCase System.out.println("Error message: " + exception.getMessage()); } } + + /** + * Checks that the current version can be serialized and deserialized. + */ + public void testSerialize() throws Exception + { + File file = TempFileProvider.createTempFile(getName(), ".bin"); + System.out.println("Test " + getName() + " writing to " + file.getPath()); + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file)); + + VersionHistoryImpl vh = testAddVersionImpl(); + try + { + os.writeObject(vh); + } + finally + { + try { os.close(); } catch (Throwable e) {} + } + ObjectInputStream is = new ObjectInputStream(new FileInputStream(file)); + VersionHistoryImpl vhObj; + try + { + vhObj = (VersionHistoryImpl) is.readObject(); + } + finally + { + try { is.close(); } catch (Throwable e) {} + } + assertNotNull(vhObj); + assertNotNull("No root version", vhObj.getRootVersion()); + assertEquals( + "Deserialized object does not match original", + vh.getRootVersion().getFrozenStateNodeRef(), + vhObj.getRootVersion().getFrozenStateNodeRef()); + } + + public static final String DESERIALIZE_V22SP4 = "classpath:version-history/VersionHistoryImplTest-testSerialize-V2.2.4.bin"; + public static final String DESERIALIZE_V310_DEV = "classpath:version-history/VersionHistoryImplTest-testSerialize-V3.1.0-dev.bin"; + public static final String DESERIALIZE_V310 = "classpath:version-history/VersionHistoryImplTest-testSerialize-V3.1.0.bin"; + /** + * @see {@link #DESERIALIZE_V22SP4} + * @see {@link #DESERIALIZE_V310_DEV} + * @see {@link #DESERIALIZE_V310} + */ + public void testDeserializeV22SP4() throws Exception + { + String[] resourceLocations = new String[] { + DESERIALIZE_V22SP4, + DESERIALIZE_V310_DEV, + DESERIALIZE_V310 + }; + for (String resourceLocation : resourceLocations) + { + Resource resource = new DefaultResourceLoader().getResource(resourceLocation); + assertNotNull("Unable to find " + resourceLocation, resource); + assertTrue("Unable to find " + resourceLocation, resource.exists()); + + @SuppressWarnings("unused") + VersionHistoryImpl vhObj; + ObjectInputStream is = new ObjectInputStream(resource.getInputStream()); + try + { + vhObj = (VersionHistoryImpl) is.readObject(); + } + finally + { + try { is.close(); } catch (Throwable e) {} + } + } + } } diff --git a/source/java/org/alfresco/repo/version/common/VersionImpl.java b/source/java/org/alfresco/repo/version/common/VersionImpl.java index f63fd8e59d..981eafbfa9 100644 --- a/source/java/org/alfresco/repo/version/common/VersionImpl.java +++ b/source/java/org/alfresco/repo/version/common/VersionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Alfresco Software Limited. + * 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 @@ -96,69 +96,61 @@ public class VersionImpl implements Version { return versionProperties.toString(); } - - - /** - * Helper method to get the created date from the version property data. - * - * @return the date the version was created (note: not the date of the original node) - */ + + public Date getFrozenModifiedDate() + { + Date modifiedDate = (Date)this.versionProperties.get(Version2Model.PROP_FROZEN_MODIFIED); + if (modifiedDate == null) + { + // Assume deprecated V1 version store + modifiedDate = (Date)this.versionProperties.get(VersionBaseModel.PROP_CREATED_DATE); + } + return modifiedDate; + } + + public String getFrozenModifier() + { + String modifier = (String)this.versionProperties.get(Version2Model.PROP_FROZEN_MODIFIER); + if (modifier == null) + { + // Assume deprecated V1 version store + modifier = (String)this.versionProperties.get(VersionBaseModel.PROP_CREATOR); + } + return modifier; + } + public Date getCreatedDate() { - return (Date)this.versionProperties.get(VersionBaseModel.PROP_CREATED_DATE); + // note: internal version node created date can be retrieved via standard node service + return getFrozenModifiedDate(); } - /** - * Helper method to get the creator from the version property data. - * - * @return the creator of the version (note: not the creator of the original node) - */ public String getCreator() { - return (String)this.versionProperties.get(VersionBaseModel.PROP_CREATOR); + // note: internal version node creator can be retrieved via standard node service + return getFrozenModifier(); } - - /** - * Helper method to get the version label from the version property data. - * - * @return the version label - */ + public String getVersionLabel() { return (String)this.versionProperties.get(VersionBaseModel.PROP_VERSION_LABEL); } - /** - * Helper method to get the version type. - * - * @return the value of the version type as an enum value - */ public VersionType getVersionType() { return (VersionType)this.versionProperties.get(VersionBaseModel.PROP_VERSION_TYPE); } - /** - * Helper method to get the version description. - * - * @return the version description - */ public String getDescription() { return (String)this.versionProperties.get(Version.PROP_DESCRIPTION); } - /** - * @see org.alfresco.service.cmr.version.Version#getVersionProperties() - */ public Map getVersionProperties() { return this.versionProperties; } - - /** - * @see org.alfresco.service.cmr.version.Version#getVersionProperty(java.lang.String) - */ + public Serializable getVersionProperty(String name) { Serializable result = null; @@ -169,9 +161,6 @@ public class VersionImpl implements Version return result; } - /** - * @see org.alfresco.service.cmr.version.Version#getVersionedNodeRef() - */ public NodeRef getVersionedNodeRef() { NodeRef versionedNodeRef = null; @@ -193,10 +182,7 @@ public class VersionImpl implements Version return versionedNodeRef; } - - /** - * @see org.alfresco.service.cmr.version.Version#getFrozenStateNodeRef() - */ + public NodeRef getFrozenStateNodeRef() { return this.nodeRef; diff --git a/source/java/org/alfresco/service/cmr/version/Version.java b/source/java/org/alfresco/service/cmr/version/Version.java index 2415ec4d72..e8cf4e5395 100644 --- a/source/java/org/alfresco/service/cmr/version/Version.java +++ b/source/java/org/alfresco/service/cmr/version/Version.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * 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 @@ -38,7 +38,7 @@ import org.alfresco.service.cmr.repository.NodeRef; * Allows access to version property values and frozen state node references. * The version history tree can also be navigated. * - * @author Roy Wetherall + * @author Roy Wetherall, janv */ public interface Version extends Serializable { @@ -51,6 +51,7 @@ public interface Version extends Serializable * Helper method to get the created date from the version property data. * * @return the date the version was created + * @deprecated use getFrozenModifiedDate */ public Date getCreatedDate(); @@ -58,8 +59,24 @@ public interface Version extends Serializable * Helper method to get the creator of the version. * * @return the creator of the version + * @deprecated use getFrozenModifier */ public String getCreator(); + + /** + * Helper method to get the frozen (original) modified date for this version of the node + * + * @return the modified date + */ + public Date getFrozenModifiedDate(); + + + /** + * Helper method to get the frozen (original) modifier for this version of the node + * + * @return the modifier + */ + public String getFrozenModifier(); /** * Helper method to get the version label from the version property data. diff --git a/source/java/org/alfresco/service/cmr/version/VersionService.java b/source/java/org/alfresco/service/cmr/version/VersionService.java index 712a74712c..5f366b79bf 100644 --- a/source/java/org/alfresco/service/cmr/version/VersionService.java +++ b/source/java/org/alfresco/service/cmr/version/VersionService.java @@ -70,7 +70,7 @@ public interface VersionService * * @param nodeRef a node reference * @param versionProperties the version properties that are stored with the newly created - * version + * version, or null if there are no relevant properties * @return the created version object * @throws ReservedVersionNameException * thrown if a reserved property name is used int he version properties diff --git a/source/java/org/alfresco/util/schemadump/Main.java b/source/java/org/alfresco/util/schemadump/Main.java index 2a263c06ea..48cc0d8e0d 100644 --- a/source/java/org/alfresco/util/schemadump/Main.java +++ b/source/java/org/alfresco/util/schemadump/Main.java @@ -24,13 +24,12 @@ */ package org.alfresco.util.schemadump; -import java.io.IOException; +import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Collections; @@ -72,32 +71,35 @@ public class Main private final Map reverseTypeMap = new TreeMap(); /** Should we scale down string field widths (assuming 4 bytes to one character?). */ - private final boolean scaleCharacters; + private boolean scaleCharacters; /** The JDBC connection. */ - private final Connection con; + private Connection con; /** - * Creates a new instance of this tool.. + * The main method. * - * @param contextPath - * path to the context xml file - * @throws SQLException - * the SQL exception - * @throws IOException - * Signals that an I/O exception has occurred. - * @throws InstantiationException - * the instantiation exception - * @throws IllegalAccessException - * the illegal access exception - * @throws ClassNotFoundException - * the class not found exception - * @throws NoSuchFieldException - * the no such field exception + * @param args + * the args: <context.xml> <output.xml> + */ + public static void main(final String[] args) throws Exception + { + if (args.length != 2) + { + System.out.println("Usage:"); + System.out.println("java " + Main.class.getName() + " "); + System.exit(1); + } + + final File outputFile = new File(args[1]); + new Main(args[0]).execute(outputFile); + } + + /** + * Creates a new instance of this tool by starting up a full context. */ @SuppressWarnings("unchecked") - public Main(final String contextPath) throws SQLException, IOException, InstantiationException, IllegalAccessException, - ClassNotFoundException, NoSuchFieldException + public Main(final String contextPath) throws Exception { final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] { @@ -109,6 +111,32 @@ public class Main // Use Java reflection to bypass accessibility rules and get hold of hibernate's type mapping! final Properties hibProps = (Properties) context.getBean("hibernateConfigProperties"); final Dialect dialect = (Dialect) Class.forName(hibProps.getProperty("hibernate.dialect")).newInstance(); + + // Initialize + init(dialect); + } + + /** + * Create a new instance of the tool within the context of an existing database connection + * + * @param connection the database connection to use for metadata queries + * @param dialect the Hibernate dialect + */ + public Main(final Connection connection, final Dialect dialect) throws Exception + { + this.con = connection; + + // Initialize + init(dialect); + } + + /** + * Initializes the fields ready to perform the database metadata reading + * @param dialect the Hibernate dialect + */ + @SuppressWarnings("unchecked") + private void init(final Dialect dialect) throws Exception + { this.scaleCharacters = dialect instanceof Oracle8iDialect; final Field typeNamesField = Dialect.class.getDeclaredField("typeNames"); typeNamesField.setAccessible(true); @@ -135,22 +163,13 @@ public class Main } /** - * The main method. + * Execute, writing the result to the given file. * - * @param args - * the args - * @throws Exception - * the exception + * @param outputFile the file to write to */ - public static void main(final String[] args) throws Exception + public void execute(File outputFile) throws Exception { - if (args.length != 2) - { - System.out.println("Usage:"); - System.out.println("java " + Main.class.getName() + " "); - System.exit(1); - } - final NamedElementCollection result = new Main(args[0]).execute(); + final NamedElementCollection result = execute(); // Set up a SAX TransformerHandler for outputting XML final SAXTransformerFactory stf = (SAXTransformerFactory) TransformerFactory.newInstance(); @@ -165,7 +184,7 @@ public class Main // It was worth a try } t.setOutputProperty(OutputKeys.INDENT, "yes"); - xmlOut.setResult(new StreamResult(args[1])); + xmlOut.setResult(new StreamResult(outputFile)); xmlOut.startDocument(); result.output(xmlOut); @@ -175,28 +194,10 @@ public class Main /** * Execute. * - * @return the named element collection - * @throws SQLException - * the SQL exception - * @throws IllegalArgumentException - * the illegal argument exception - * @throws IllegalAccessException - * the illegal access exception - * @throws IOException - * Signals that an I/O exception has occurred. - * @throws InstantiationException - * the instantiation exception - * @throws ClassNotFoundException - * the class not found exception - * @throws SecurityException - * the security exception - * @throws NoSuchFieldException - * the no such field exception + * @return Returns the named XML elements */ - public NamedElementCollection execute() throws SQLException, IllegalArgumentException, IllegalAccessException, - IOException, InstantiationException, ClassNotFoundException, SecurityException, NoSuchFieldException + public NamedElementCollection execute() throws Exception { - final NamedElementCollection schemaCol = new NamedElementCollection("schema", "table"); final DatabaseMetaData dbmd = this.con.getMetaData(); diff --git a/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V2.2.4.bin b/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V2.2.4.bin new file mode 100644 index 0000000000..ca1baecbfe Binary files /dev/null and b/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V2.2.4.bin differ diff --git a/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V3.1.0-dev.bin b/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V3.1.0-dev.bin new file mode 100644 index 0000000000..b579111c1b Binary files /dev/null and b/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V3.1.0-dev.bin differ diff --git a/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V3.1.0.bin b/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V3.1.0.bin new file mode 100644 index 0000000000..6b2328d4f9 Binary files /dev/null and b/source/test-resources/version-history/VersionHistoryImplTest-testSerialize-V3.1.0.bin differ