mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-09-17 14:21:39 +00:00
REPO-4180: Service Pack: MNT-20305 Oracle schema validation check failure with ojdbc7.jar version 12.1.0.2
- fixed regression in SchemaBootstrap that cause the schema validation to not be done - added support for custom sequences retrieval (active if only schema reference sequences file exists) - added dialect and script utility classes - refactored ExportDb, ScriptExecutorImpl and SchemaBootstrap to use the utility classes to reduce code duplication
This commit is contained in:
@@ -52,7 +52,6 @@ import java.sql.SQLXML;
|
|||||||
import java.sql.Savepoint;
|
import java.sql.Savepoint;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.sql.Struct;
|
import java.sql.Struct;
|
||||||
import java.sql.Types;
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -82,6 +81,7 @@ import org.alfresco.repo.domain.patch.AppliedPatchDAO;
|
|||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
import org.alfresco.service.descriptor.DescriptorService;
|
import org.alfresco.service.descriptor.DescriptorService;
|
||||||
import org.alfresco.util.DatabaseMetaDataHelper;
|
import org.alfresco.util.DatabaseMetaDataHelper;
|
||||||
|
import org.alfresco.util.DialectUtil;
|
||||||
import org.alfresco.util.LogUtil;
|
import org.alfresco.util.LogUtil;
|
||||||
import org.alfresco.util.PropertyCheck;
|
import org.alfresco.util.PropertyCheck;
|
||||||
import org.alfresco.util.TempFileProvider;
|
import org.alfresco.util.TempFileProvider;
|
||||||
@@ -111,9 +111,6 @@ import org.springframework.extensions.surf.util.I18NUtil;
|
|||||||
*/
|
*/
|
||||||
public class SchemaBootstrap extends AbstractLifecycleBean
|
public class SchemaBootstrap extends AbstractLifecycleBean
|
||||||
{
|
{
|
||||||
/** The placeholder for the configured <code>Dialect</code> class name: <b>${db.script.dialect}</b> */
|
|
||||||
private static final String PLACEHOLDER_DIALECT = "\\$\\{db\\.script\\.dialect\\}";
|
|
||||||
|
|
||||||
/** The global property containing the default batch size used by --FOREACH */
|
/** The global property containing the default batch size used by --FOREACH */
|
||||||
private static final String PROPERTY_DEFAULT_BATCH_SIZE = "system.upgrade.default.batchsize";
|
private static final String PROPERTY_DEFAULT_BATCH_SIZE = "system.upgrade.default.batchsize";
|
||||||
|
|
||||||
@@ -323,7 +320,6 @@ public class SchemaBootstrap extends AbstractLifecycleBean
|
|||||||
* type (e.g. PostgreSQL)
|
* type (e.g. PostgreSQL)
|
||||||
*
|
*
|
||||||
* @param schemaReferenceUrls the schemaReferenceUrls to set
|
* @param schemaReferenceUrls the schemaReferenceUrls to set
|
||||||
* @see #PLACEHOLDER_DIALECT
|
|
||||||
*/
|
*/
|
||||||
public void setSchemaReferenceUrls(List<String> schemaReferenceUrls)
|
public void setSchemaReferenceUrls(List<String> schemaReferenceUrls)
|
||||||
{
|
{
|
||||||
@@ -1155,64 +1151,11 @@ public class SchemaBootstrap extends AbstractLifecycleBean
|
|||||||
try { scriptInputStream.close(); } catch (Throwable e) {} // usually a duplicate close
|
try { scriptInputStream.close(); } catch (Throwable e) {} // usually a duplicate close
|
||||||
}
|
}
|
||||||
// now execute it
|
// now execute it
|
||||||
String dialectScriptUrl = scriptUrl.replaceAll(PLACEHOLDER_DIALECT, dialect.getClass().getName());
|
String dialectScriptUrl = scriptUrl.replaceAll(DialectUtil.PLACEHOLDER_DIALECT, dialect.getClass().getName());
|
||||||
// Replace the script placeholders
|
// Replace the script placeholders
|
||||||
executeScriptFile(connection, tempFile, dialectScriptUrl);
|
executeScriptFile(connection, tempFile, dialectScriptUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the dialect placeholder in the resource URL and attempts to find a file for
|
|
||||||
* it. If not found, the dialect hierarchy will be walked until a compatible resource is
|
|
||||||
* found. This makes it possible to have resources that are generic to all dialects.
|
|
||||||
*
|
|
||||||
* @return The Resource, otherwise null
|
|
||||||
*/
|
|
||||||
private Resource getDialectResource(Class<?> dialectClass, String resourceUrl)
|
|
||||||
{
|
|
||||||
// replace the dialect placeholder
|
|
||||||
String dialectResourceUrl = resolveDialectUrl(dialectClass, resourceUrl);
|
|
||||||
// get a handle on the resource
|
|
||||||
Resource resource = rpr.getResource(dialectResourceUrl);
|
|
||||||
if (!resource.exists())
|
|
||||||
{
|
|
||||||
// it wasn't found. Get the superclass of the dialect and try again
|
|
||||||
Class<?> superClass = dialectClass.getSuperclass();
|
|
||||||
if (Dialect.class.isAssignableFrom(superClass))
|
|
||||||
{
|
|
||||||
// we still have a Dialect - try again
|
|
||||||
return getDialectResource(superClass, resourceUrl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we have exhausted all options
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we have a handle to it
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes resource URL containing the {@link SchemaBootstrap#PLACEHOLDER_DIALECT dialect placeholder text}
|
|
||||||
* and substitutes the placeholder with the name of the given dialect's class.
|
|
||||||
* <p/>
|
|
||||||
* For example:
|
|
||||||
* <pre>
|
|
||||||
* resolveDialectUrl(MySQLInnoDBDialect.class, "classpath:alfresco/db/${db.script.dialect}/myfile.xml")
|
|
||||||
* </pre>
|
|
||||||
* would give the following String:
|
|
||||||
* <pre>
|
|
||||||
* classpath:alfresco/db/org.hibernate.dialect.MySQLInnoDBDialect/myfile.xml
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
private String resolveDialectUrl(Class<?> dialectClass, String resourceUrl)
|
|
||||||
{
|
|
||||||
return resourceUrl.replaceAll(PLACEHOLDER_DIALECT, dialectClass.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the dialect placeholder in the script URL and attempts to find a file for
|
* Replaces the dialect placeholder in the script URL and attempts to find a file for
|
||||||
* it. If not found, the dialect hierarchy will be walked until a compatible script is
|
* it. If not found, the dialect hierarchy will be walked until a compatible script is
|
||||||
@@ -1222,7 +1165,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean
|
|||||||
*/
|
*/
|
||||||
private InputStream getScriptInputStream(Class<?> dialectClazz, String scriptUrl) throws Exception
|
private InputStream getScriptInputStream(Class<?> dialectClazz, String scriptUrl) throws Exception
|
||||||
{
|
{
|
||||||
Resource resource = getDialectResource(dialectClazz, scriptUrl);
|
Resource resource = DialectUtil.getDialectResource(rpr, dialectClazz, scriptUrl);
|
||||||
if (resource == null)
|
if (resource == null)
|
||||||
{
|
{
|
||||||
throw new AlfrescoRuntimeException("Script [ " + scriptUrl + " ] can't be found for " + dialectClazz);
|
throw new AlfrescoRuntimeException("Script [ " + scriptUrl + " ] can't be found for " + dialectClazz);
|
||||||
@@ -1749,8 +1692,12 @@ public class SchemaBootstrap extends AbstractLifecycleBean
|
|||||||
{
|
{
|
||||||
// Remove the flag indicating a running bootstrap
|
// Remove the flag indicating a running bootstrap
|
||||||
setBootstrapCompleted(connection);
|
setBootstrapCompleted(connection);
|
||||||
|
|
||||||
|
// Validate the schema, post-upgrade
|
||||||
|
validateSchema("Alfresco-{0}-Validation-Post-Upgrade-{1}-", null);
|
||||||
|
// 4.0+ schema dump
|
||||||
|
dumpSchema("post-upgrade");
|
||||||
}
|
}
|
||||||
reportNormalizedDumps();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1804,17 +1751,6 @@ public class SchemaBootstrap extends AbstractLifecycleBean
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportNormalizedDumps()
|
|
||||||
{
|
|
||||||
if (executedStatementsThreadLocal.get() != null)
|
|
||||||
{
|
|
||||||
// Validate the schema, post-upgrade
|
|
||||||
validateSchema("Alfresco-{0}-Validation-Post-Upgrade-{1}-", null);
|
|
||||||
// 4.0+ schema dump
|
|
||||||
dumpSchema("post-upgrade");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeLogsWithDBStatementExecuted()
|
private void writeLogsWithDBStatementExecuted()
|
||||||
{
|
{
|
||||||
// Copy the executed statements to the output file
|
// Copy the executed statements to the output file
|
||||||
@@ -1861,11 +1797,11 @@ public class SchemaBootstrap extends AbstractLifecycleBean
|
|||||||
// and process each in turn.
|
// and process each in turn.
|
||||||
for (String schemaReferenceUrl : schemaReferenceUrls)
|
for (String schemaReferenceUrl : schemaReferenceUrls)
|
||||||
{
|
{
|
||||||
Resource referenceResource = getDialectResource(dialect.getClass(), schemaReferenceUrl);
|
Resource referenceResource = DialectUtil.getDialectResource(rpr, dialect.getClass(), schemaReferenceUrl);
|
||||||
|
|
||||||
if (referenceResource == null || !referenceResource.exists())
|
if (referenceResource == null || !referenceResource.exists())
|
||||||
{
|
{
|
||||||
String resourceUrl = resolveDialectUrl(dialect.getClass(), schemaReferenceUrl);
|
String resourceUrl = DialectUtil.resolveDialectUrl(dialect.getClass(), schemaReferenceUrl);
|
||||||
LogUtil.debug(logger, DEBUG_SCHEMA_COMP_NO_REF_FILE, resourceUrl);
|
LogUtil.debug(logger, DEBUG_SCHEMA_COMP_NO_REF_FILE, resourceUrl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -47,6 +47,7 @@ import org.alfresco.repo.domain.dialect.Dialect;
|
|||||||
import org.alfresco.repo.domain.dialect.MySQLInnoDBDialect;
|
import org.alfresco.repo.domain.dialect.MySQLInnoDBDialect;
|
||||||
import org.alfresco.repo.domain.dialect.PostgreSQLDialect;
|
import org.alfresco.repo.domain.dialect.PostgreSQLDialect;
|
||||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||||
|
import org.alfresco.util.DialectUtil;
|
||||||
import org.alfresco.util.LogUtil;
|
import org.alfresco.util.LogUtil;
|
||||||
import org.alfresco.util.TempFileProvider;
|
import org.alfresco.util.TempFileProvider;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
@@ -58,8 +59,6 @@ import org.springframework.core.io.support.ResourcePatternResolver;
|
|||||||
|
|
||||||
public class ScriptExecutorImpl implements ScriptExecutor
|
public class ScriptExecutorImpl implements ScriptExecutor
|
||||||
{
|
{
|
||||||
/** The placeholder for the configured <code>Dialect</code> class name: <b>${db.script.dialect}</b> */
|
|
||||||
private static final String PLACEHOLDER_DIALECT = "\\$\\{db\\.script\\.dialect\\}";
|
|
||||||
/** The global property containing the default batch size used by --FOREACH */
|
/** The global property containing the default batch size used by --FOREACH */
|
||||||
private static final String PROPERTY_DEFAULT_BATCH_SIZE = "system.upgrade.default.batchsize";
|
private static final String PROPERTY_DEFAULT_BATCH_SIZE = "system.upgrade.default.batchsize";
|
||||||
private static final String MSG_EXECUTING_GENERATED_SCRIPT = "schema.update.msg.executing_generated_script";
|
private static final String MSG_EXECUTING_GENERATED_SCRIPT = "schema.update.msg.executing_generated_script";
|
||||||
@@ -186,68 +185,11 @@ public class ScriptExecutorImpl implements ScriptExecutor
|
|||||||
try { scriptInputStream.close(); } catch (Throwable e) {} // usually a duplicate close
|
try { scriptInputStream.close(); } catch (Throwable e) {} // usually a duplicate close
|
||||||
}
|
}
|
||||||
// now execute it
|
// now execute it
|
||||||
String dialectScriptUrl = scriptUrl.replaceAll(PLACEHOLDER_DIALECT, dialect.getClass().getName());
|
String dialectScriptUrl = scriptUrl.replaceAll(DialectUtil.PLACEHOLDER_DIALECT, dialect.getClass().getName());
|
||||||
// Replace the script placeholders
|
// Replace the script placeholders
|
||||||
executeScriptFile(connection, tempFile, dialectScriptUrl);
|
executeScriptFile(connection, tempFile, dialectScriptUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the dialect placeholder in the resource URL and attempts to find a file for
|
|
||||||
* it. If not found, the dialect hierarchy will be walked until a compatible resource is
|
|
||||||
* found. This makes it possible to have resources that are generic to all dialects.
|
|
||||||
*
|
|
||||||
* @return The Resource, otherwise null
|
|
||||||
*/
|
|
||||||
private Resource getDialectResource(Class dialectClass, String resourceUrl)
|
|
||||||
{
|
|
||||||
// replace the dialect placeholder
|
|
||||||
String dialectResourceUrl = resolveDialectUrl(dialectClass, resourceUrl);
|
|
||||||
// get a handle on the resource
|
|
||||||
Resource resource = rpr.getResource(dialectResourceUrl);
|
|
||||||
if (!resource.exists())
|
|
||||||
{
|
|
||||||
// it wasn't found. Get the superclass of the dialect and try again
|
|
||||||
Class superClass = dialectClass.getSuperclass();
|
|
||||||
if (Dialect.class.isAssignableFrom(superClass))
|
|
||||||
{
|
|
||||||
// we still have a Dialect - try again
|
|
||||||
return getDialectResource(superClass, resourceUrl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we have exhausted all options
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we have a handle to it
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes resource URL containing the {@link ScriptExecutorImpl#PLACEHOLDER_DIALECT dialect placeholder text}
|
|
||||||
* and substitutes the placeholder with the name of the given dialect's class.
|
|
||||||
* <p/>
|
|
||||||
* For example:
|
|
||||||
* <pre>
|
|
||||||
* resolveDialectUrl(MySQLInnoDBDialect.class, "classpath:alfresco/db/${db.script.dialect}/myfile.xml")
|
|
||||||
* </pre>
|
|
||||||
* would give the following String:
|
|
||||||
* <pre>
|
|
||||||
* classpath:alfresco/db/org.hibernate.dialect.MySQLInnoDBDialect/myfile.xml
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @param dialectClass Class
|
|
||||||
* @param resourceUrl String
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
private String resolveDialectUrl(Class dialectClass, String resourceUrl)
|
|
||||||
{
|
|
||||||
return resourceUrl.replaceAll(PLACEHOLDER_DIALECT, dialectClass.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the dialect placeholder in the script URL and attempts to find a file for
|
* Replaces the dialect placeholder in the script URL and attempts to find a file for
|
||||||
* it. If not found, the dialect hierarchy will be walked until a compatible script is
|
* it. If not found, the dialect hierarchy will be walked until a compatible script is
|
||||||
@@ -257,7 +199,7 @@ public class ScriptExecutorImpl implements ScriptExecutor
|
|||||||
*/
|
*/
|
||||||
private InputStream getScriptInputStream(Class dialectClazz, String scriptUrl) throws Exception
|
private InputStream getScriptInputStream(Class dialectClazz, String scriptUrl) throws Exception
|
||||||
{
|
{
|
||||||
Resource resource = getDialectResource(dialectClazz, scriptUrl);
|
Resource resource = DialectUtil.getDialectResource(rpr, dialectClazz, scriptUrl);
|
||||||
if (resource == null)
|
if (resource == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
107
src/main/java/org/alfresco/util/DBScriptUtil.java
Normal file
107
src/main/java/org/alfresco/util/DBScriptUtil.java
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.LineNumberReader;
|
||||||
|
|
||||||
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
|
|
||||||
|
public abstract class DBScriptUtil
|
||||||
|
{
|
||||||
|
private static final String DEFAULT_SCRIPT_COMMENT_PREFIX = "--";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a script from the provided EncodedResource and build a String containing
|
||||||
|
* the lines.
|
||||||
|
*
|
||||||
|
* @param resource
|
||||||
|
* the resource (potentially associated with a specific encoding) to
|
||||||
|
* load the SQL script from
|
||||||
|
* @return a String containing the script lines
|
||||||
|
*/
|
||||||
|
public static String readScript(EncodedResource resource) throws IOException
|
||||||
|
{
|
||||||
|
return readScript(resource, DEFAULT_SCRIPT_COMMENT_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a script from the provided EncodedResource, using the supplied line
|
||||||
|
* comment prefix, and build a String containing the lines.
|
||||||
|
*
|
||||||
|
* @param resource
|
||||||
|
* the resource (potentially associated with a specific encoding) to
|
||||||
|
* load the SQL script from
|
||||||
|
* @param lineCommentPrefix
|
||||||
|
* the prefix that identifies comments in the SQL script (typically
|
||||||
|
* "--")
|
||||||
|
* @return a String containing the script lines
|
||||||
|
*/
|
||||||
|
private static String readScript(EncodedResource resource, String lineCommentPrefix) throws IOException
|
||||||
|
{
|
||||||
|
LineNumberReader lineNumberReader = new LineNumberReader(resource.getReader());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return readScript(lineNumberReader, lineCommentPrefix);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
lineNumberReader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a script from the provided LineNumberReader, using the supplied line
|
||||||
|
* comment prefix, and build a String containing the lines.
|
||||||
|
*
|
||||||
|
* @param lineNumberReader
|
||||||
|
* the LineNumberReader containing the script to be processed
|
||||||
|
* @param lineCommentPrefix
|
||||||
|
* the prefix that identifies comments in the SQL script (typically
|
||||||
|
* "--")
|
||||||
|
* @return a String containing the script lines
|
||||||
|
*/
|
||||||
|
private static String readScript(LineNumberReader lineNumberReader, String lineCommentPrefix) throws IOException
|
||||||
|
{
|
||||||
|
String statement = lineNumberReader.readLine();
|
||||||
|
StringBuilder scriptBuilder = new StringBuilder();
|
||||||
|
while (statement != null)
|
||||||
|
{
|
||||||
|
if (lineCommentPrefix != null && !statement.startsWith(lineCommentPrefix))
|
||||||
|
{
|
||||||
|
if (scriptBuilder.length() > 0)
|
||||||
|
{
|
||||||
|
scriptBuilder.append('\n');
|
||||||
|
}
|
||||||
|
scriptBuilder.append(statement);
|
||||||
|
}
|
||||||
|
statement = lineNumberReader.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return scriptBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
94
src/main/java/org/alfresco/util/DialectUtil.java
Normal file
94
src/main/java/org/alfresco/util/DialectUtil.java
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* Alfresco Repository
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
* %%
|
||||||
|
* This file is part of the Alfresco software.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package org.alfresco.util;
|
||||||
|
|
||||||
|
import org.alfresco.repo.domain.dialect.Dialect;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
|
|
||||||
|
public abstract class DialectUtil
|
||||||
|
{
|
||||||
|
/** The placeholder for the configured <code>Dialect</code> class name: <b>${db.script.dialect}</b> */
|
||||||
|
public static final String PLACEHOLDER_DIALECT = "\\$\\{db\\.script\\.dialect\\}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the dialect placeholder in the resource URL and attempts to find a
|
||||||
|
* file for it. If not found, the dialect hierarchy will be walked until a
|
||||||
|
* compatible resource is found. This makes it possible to have resources that
|
||||||
|
* are generic to all dialects.
|
||||||
|
*
|
||||||
|
* @return The Resource, otherwise null
|
||||||
|
*/
|
||||||
|
public static Resource getDialectResource(ResourcePatternResolver resourcePatternResolver, Class<?> dialectClass, String resourceUrl)
|
||||||
|
{
|
||||||
|
// replace the dialect placeholder
|
||||||
|
String dialectResourceUrl = resolveDialectUrl(dialectClass, resourceUrl);
|
||||||
|
// get a handle on the resource
|
||||||
|
Resource resource = resourcePatternResolver.getResource(dialectResourceUrl);
|
||||||
|
if (!resource.exists())
|
||||||
|
{
|
||||||
|
// it wasn't found. Get the superclass of the dialect and try again
|
||||||
|
Class<?> superClass = dialectClass.getSuperclass();
|
||||||
|
if (Dialect.class.isAssignableFrom(superClass))
|
||||||
|
{
|
||||||
|
// we still have a Dialect - try again
|
||||||
|
return getDialectResource(resourcePatternResolver, superClass, resourceUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we have exhausted all options
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we have a handle to it
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes resource URL containing the {@link DialectUtil#PLACEHOLDER_DIALECT
|
||||||
|
* dialect placeholder text} and substitutes the placeholder with the name of
|
||||||
|
* the given dialect's class.
|
||||||
|
* <p/>
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* resolveDialectUrl(MySQLInnoDBDialect.class, "classpath:alfresco/db/${db.script.dialect}/myfile.xml")
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* would give the following String:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* classpath:alfresco/db/org.hibernate.dialect.MySQLInnoDBDialect/myfile.xml
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public static String resolveDialectUrl(Class<?> dialectClass, String resourceUrl)
|
||||||
|
{
|
||||||
|
return resourceUrl.replaceAll(PLACEHOLDER_DIALECT, dialectClass.getName());
|
||||||
|
}
|
||||||
|
}
|
@@ -25,9 +25,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.util.schemacomp;
|
package org.alfresco.util.schemacomp;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DatabaseMetaData;
|
import java.sql.DatabaseMetaData;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -40,7 +42,9 @@ import org.alfresco.repo.domain.dialect.TypeNames;
|
|||||||
import org.alfresco.service.descriptor.Descriptor;
|
import org.alfresco.service.descriptor.Descriptor;
|
||||||
import org.alfresco.service.descriptor.DescriptorService;
|
import org.alfresco.service.descriptor.DescriptorService;
|
||||||
import org.alfresco.util.DatabaseMetaDataHelper;
|
import org.alfresco.util.DatabaseMetaDataHelper;
|
||||||
|
import org.alfresco.util.DialectUtil;
|
||||||
import org.alfresco.util.PropertyCheck;
|
import org.alfresco.util.PropertyCheck;
|
||||||
|
import org.alfresco.util.DBScriptUtil;
|
||||||
import org.alfresco.util.schemacomp.model.Column;
|
import org.alfresco.util.schemacomp.model.Column;
|
||||||
import org.alfresco.util.schemacomp.model.ForeignKey;
|
import org.alfresco.util.schemacomp.model.ForeignKey;
|
||||||
import org.alfresco.util.schemacomp.model.Index;
|
import org.alfresco.util.schemacomp.model.Index;
|
||||||
@@ -51,7 +55,10 @@ import org.alfresco.util.schemacomp.model.Table;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports a database schema to an in-memory {@link Schema} object.
|
* Exports a database schema to an in-memory {@link Schema} object.
|
||||||
@@ -60,6 +67,8 @@ import org.springframework.context.ApplicationContext;
|
|||||||
*/
|
*/
|
||||||
public class ExportDb
|
public class ExportDb
|
||||||
{
|
{
|
||||||
|
private static final String SCHEMA_REFERENCE_SEQUENCES_SQL_FILE = "classpath:alfresco/dbscripts/create/${db.script.dialect}/Schema-Reference-Sequences.sql";
|
||||||
|
|
||||||
/** Reverse map from database types to JDBC types (loaded from a Hibernate dialect). */
|
/** Reverse map from database types to JDBC types (loaded from a Hibernate dialect). */
|
||||||
private final Map<String, Integer> reverseTypeMap = new TreeMap<String, Integer>();
|
private final Map<String, Integer> reverseTypeMap = new TreeMap<String, Integer>();
|
||||||
|
|
||||||
@@ -86,6 +95,7 @@ public class ExportDb
|
|||||||
|
|
||||||
private final static Log log = LogFactory.getLog(ExportDb.class);
|
private final static Log log = LogFactory.getLog(ExportDb.class);
|
||||||
|
|
||||||
|
private ResourcePatternResolver rpr = new PathMatchingResourcePatternResolver(this.getClass().getClassLoader());
|
||||||
|
|
||||||
public ExportDb(ApplicationContext context)
|
public ExportDb(ApplicationContext context)
|
||||||
{
|
{
|
||||||
@@ -227,19 +237,38 @@ public class ExportDb
|
|||||||
|
|
||||||
|
|
||||||
private void extractSchema(DatabaseMetaData dbmd, String schemaName, String prefixFilter)
|
private void extractSchema(DatabaseMetaData dbmd, String schemaName, String prefixFilter)
|
||||||
throws SQLException, IllegalArgumentException, IllegalAccessException
|
throws SQLException, IllegalArgumentException, IllegalAccessException, IOException
|
||||||
{
|
{
|
||||||
if (log.isDebugEnabled())
|
if (log.isDebugEnabled())
|
||||||
{
|
{
|
||||||
log.debug("Retrieving tables: schemaName=[" + schemaName + "], prefixFilter=[" + prefixFilter + "]");
|
log.debug("Retrieving tables: schemaName=[" + schemaName + "], prefixFilter=[" + prefixFilter + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String[] tableTypes;
|
||||||
final ResultSet tables = dbmd.getTables(null, schemaName, prefixFilter, new String[]
|
Resource sequencesRefResource = getSequencesReferenceResource();
|
||||||
|
if (sequencesRefResource != null && sequencesRefResource.exists())
|
||||||
{
|
{
|
||||||
"TABLE", "VIEW", "SEQUENCE"
|
tableTypes = new String[] {"TABLE", "VIEW"};
|
||||||
});
|
|
||||||
|
|
||||||
|
retrieveAndProcessSequences(dbmd, sequencesRefResource, schemaName, prefixFilter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tableTypes = new String[] { "TABLE", "VIEW", "SEQUENCE" };
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResultSet tables = dbmd.getTables(null, schemaName, prefixFilter, tableTypes);
|
||||||
|
|
||||||
|
processTables(dbmd, tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processTables(DatabaseMetaData dbmd, ResultSet tables)
|
||||||
|
throws SQLException, IllegalArgumentException, IllegalAccessException
|
||||||
|
{
|
||||||
|
if (tables == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (tables.next())
|
while (tables.next())
|
||||||
{
|
{
|
||||||
@@ -512,4 +541,101 @@ public class ExportDb
|
|||||||
{
|
{
|
||||||
this.dbSchemaName = dbSchemaName;
|
this.dbSchemaName = dbSchemaName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to find the schema reference sequences sql file. If not found, the
|
||||||
|
* dialect hierarchy will be walked until a compatible resource is found. This
|
||||||
|
* makes it possible to have resources that are generic to all dialects.
|
||||||
|
*
|
||||||
|
* @return The Resource, otherwise null
|
||||||
|
*/
|
||||||
|
private Resource getSequencesReferenceResource()
|
||||||
|
{
|
||||||
|
return DialectUtil.getDialectResource(rpr, dialect.getClass(), SCHEMA_REFERENCE_SEQUENCES_SQL_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves and process DB sequences for the schema validation check.
|
||||||
|
*
|
||||||
|
* @param dbmd
|
||||||
|
* the database meta data
|
||||||
|
* @param resource
|
||||||
|
* the resource to load the SQL script from
|
||||||
|
* @param schemaName
|
||||||
|
* the DB schema name
|
||||||
|
* @param prefixFilter
|
||||||
|
* the DB tables prefix filter
|
||||||
|
*/
|
||||||
|
private void retrieveAndProcessSequences(DatabaseMetaData dbmd, Resource resource, String schemaName, String prefixFilter)
|
||||||
|
throws SQLException, IllegalArgumentException, IllegalAccessException, IOException
|
||||||
|
{
|
||||||
|
final String script = DBScriptUtil.readScript(new EncodedResource(resource, "UTF-8"));
|
||||||
|
|
||||||
|
if (!script.isEmpty())
|
||||||
|
{
|
||||||
|
retrieveAndProcessSequences(dbmd, script, schemaName, prefixFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves and process DB sequences for the schema validation check.
|
||||||
|
*
|
||||||
|
* @param dbmd
|
||||||
|
* the database meta data
|
||||||
|
* @param script
|
||||||
|
* the SQL script
|
||||||
|
* @param schemaName
|
||||||
|
* the DB schema name
|
||||||
|
* @param prefixFilter
|
||||||
|
* the DB tables prefix filter
|
||||||
|
*/
|
||||||
|
private void retrieveAndProcessSequences(DatabaseMetaData dbmd, String script, String schemaName, String prefixFilter)
|
||||||
|
throws SQLException, IllegalArgumentException, IllegalAccessException
|
||||||
|
{
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("Retrieving DB sequences...");
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedStatement stmt = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stmt = dbmd.getConnection().prepareStatement(script);
|
||||||
|
stmt.setString(1, schemaName);
|
||||||
|
stmt.setString(2, prefixFilter);
|
||||||
|
|
||||||
|
boolean haveResults = stmt.execute();
|
||||||
|
|
||||||
|
if (haveResults)
|
||||||
|
{
|
||||||
|
ResultSet sequences = stmt.getResultSet();
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("Sequences processing started...");
|
||||||
|
}
|
||||||
|
|
||||||
|
processTables(dbmd, sequences);
|
||||||
|
|
||||||
|
if (log.isDebugEnabled())
|
||||||
|
{
|
||||||
|
log.debug("Sequences processing completed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (stmt != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stmt.close();
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
// Little can be done at this stage.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user