/**
* Copyright 2007 ATG DUST Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
package atg.adapter.gsa;
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.Logger;
import atg.adapter.gsa.xml.TemplateParser;
import atg.adapter.gsa.xml.VersioningContextUtil;
import atg.adapter.version.VersionRepository;
import atg.junit.nucleus.TestUtils;
import atg.naming.NameContextBindingEvent;
import atg.nucleus.Configuration;
import atg.nucleus.GenericService;
import atg.nucleus.NucleusNameResolver;
import atg.nucleus.ServiceEvent;
import atg.nucleus.ServiceException;
import atg.nucleus.logging.LogListener;
import atg.repository.RepositoryException;
/**
* This class is an extension of atg.adapter.version.VersionRepository.
* Currently, it does not support to create or drop tables based on
* startSQLRepository SQL. It only support to create or drop
* tables through specified sql scripts.
*
* For example:
*
* sqlCreateFiles=oracle={atg.dynamo.root}/SystemTests/bizuiTest/sql/install/oracle/testrepver_ddl.sql
* sqlDropFiles=oracle={atg.dynamo.root}/SystemTests/bizuiTest/sql/install/oracle/drop_testrepver_ddl.sql
*
* Additionally, it uses the GSA's import facility to allow data to be loaded
* into the tables after creation. But you have to give relevant arguments.
* There are two set of arguments you can choose.
*
* 1. specify projectName, user and comment/doCheckin(=false)
*
* For example:
*
* importFiles={atg.dynamo.root}/SystemTests/BizuiTest/data/test-repository-data.xml
* projectName=newproject
* user=publishing
* comment=ImportDataWithProjectName
*
* 2. specify workspaceIdPrefix, branchId, comment/doCheckin(=false)
*
* For example:
*
* importFiles={atg.dynamo.root}/SystemTests/BizuiTest/data/test-repository-data.xml
* workspaceIdPrefix=TestRepImport
* branchId=main
* comment=hello
* doCheckin=true
*
* @author adwivedi
* @author qma
*
* Note: this class is modified from InitializingGSA.java. It has most properties of InitializingGSA.
* You can see the detail info from the following link:
* http://atlas.atg.com/mfrenzel/apiauto/APIAutoTest.html#initializinggsa
*
*/
public class InitializingVersionRepository extends VersionRepository {
private static Logger log = Logger.getLogger(InitializingVersionRepository.class);
// -----------------------------------
// ---From Properties File------------
// do we want to create tables if they don't exist
private boolean mCreateTables = true;
public void setCreateTables(boolean pCreate) {
mCreateTables = pCreate;
}
public boolean isCreateTables() {
return mCreateTables;
}
// do we want to drop tables that exist if we want to create
// a table with the same name
private boolean mDropTables = false;
public void setDropTablesIfExist(boolean pDrop) {
mDropTables = pDrop;
}
public boolean isDropTablesIfExist() {
return mDropTables;
}
// the XML files containing export data from the TemplateParser
// it will be imported into the database after tables are created
// we load the files as Files instead of XMLFiles because the
// TemplateParser requires a full file path to the import file
// instead of using the CONFIGPATH
private File[] mImportFiles = null;
public void setImportFiles(File[] pFiles) {
mImportFiles = pFiles;
}
public File[] getImportFiles() {
return mImportFiles;
}
public String[] getImportFilesAsStrings() {
File[] f = getImportFiles();
if (f == null)
return null;
List<String> v = new ArrayList<String>();
for (int i = 0; i < f.length; i++) {
if (!v.contains(f[i].getAbsolutePath()))
v.add(f[i].getAbsolutePath());
}
return (String[]) v.toArray(new String[v.size()]);
}
// do we want to strip the 'references(..)' statements from SQL
// created by the GSA
private boolean mStripReferences = false;
public void setStripReferences(boolean pStrip) {
mStripReferences = pStrip;
}
public boolean isStripReferences() {
return mStripReferences;
}
// do we want to show the create table statements that are executed
private boolean mShowCreate = false;
public void setloggingCreateTables(boolean pLog) {
mShowCreate = pLog;
}
public boolean isLoggingCreateTables() {
return mShowCreate;
}
/* the SQLProcessorEngine to use for creating tables
* this property is optional because we'll create a default
* SQLProcessorEngine if the property isn't set
*/
private SQLProcessorEngine mSQLProcessor = null;
public void setSQLProcessor(SQLProcessorEngine pEngine) {
mSQLProcessor = pEngine;
}
public SQLProcessorEngine getSQLProcessor() {
// create a new processor if one isn't set
if (mSQLProcessor == null) {
mSQLProcessor = new SQLProcessorEngine(this);
mSQLProcessor.setLoggingDebug(this.isLoggingDebug());
mSQLProcessor.setLoggingError(this.isLoggingError());
mSQLProcessor.setLoggingInfo(this.isLoggingInfo());
mSQLProcessor.setLoggingWarning(this.isLoggingWarning());
LogListener[] listeners = this.getLogListeners();
for (int i = 0; i < listeners.length; i++) {
mSQLProcessor.addLogListener(listeners[i]);
}
}
return mSQLProcessor;
}
/** boolean indicating whether we should perform the import every time
* Dynamo starts, or only perform the import if we created at least
* one table.
* NOTE: if dropTablesIfExist is true, the import will occur every time
* because we will create tables every time.
* default: false - only perform the import when tables are created
*/
private boolean mImportEveryStartup = false;
public void setImportEveryStartup(boolean pImport) {
mImportEveryStartup = pImport;
}
public boolean isImportEveryStartup() {
return mImportEveryStartup;
}
/** boolean indicating whether we should drop all tables associated with
* this repository when Dynamo is shut down.
* NOTE: this will only work properly is Dynamo is shutdown properly. It
* will not work if Dynamo is just killed
* default: false
*/
private boolean mDropTablesAtShutdown = false;
public void setDropTablesAtShutdown(boolean pDrop) {
mDropTablesAtShutdown = pDrop;
}
public boolean isDropTablesAtShutdown() {
return mDropTablesAtShutdown;
}
/** boolean indicating whether to wrap each imported file in it's own transaction.
* this is a new option in D5.5 that has changed the method signature of
* atg.adapter.gsa.xml.TemplateParser.importFiles()
* default: true
*/
private boolean mImportWithTransaction = true;
public void setImportWithTransaction(boolean pTran) {
mImportWithTransaction = pTran;
}
public boolean isImportWithTransaction() {
return mImportWithTransaction;
}
private Properties mSqlCreateFiles = new Properties();
/** Optional mapping of user-specified sql files that should be executed instead of
* the SQL generated by startSQLRepository. Key values must be one of (case sensitive):
* <b>default</b>, <b>oracle</b>, <b>solid</b>, <b>informix</b>, <b>microsoft</b>,
* <b>sybase</b>, or <b>db2</b>. Mapped values should be a colon (:) separated
* ordered list of files to execute for that database type.
* <br>Specified files may use <pre>{....}</pre> notation to indicate a
* System variable that should be substituted at runtime, such as <pre>{atg.dynamo.root}</pre>.
* <p>The following behavior is observed:
* <pre>
* a) database meta data is used to determine specific database type
* b) when <b>default</b> not specified
* - if mapping exists for specific db type, those files are executed
* - otherwise, output from startSQLRepository is executed
* c) when <b>default</b> is specified
* - if mapping exists for specific db type, those files are executed
* - otherwise, files mapped under default are executed
* d) if a mapping exists for a db type in 'sqlCreateFiles' then a corresponding
* entry (to the specific db type, or to default) must exist. Otherwise an exception
* is thrown at starup.
* </pre>
* <p>Also, when a file specified in the property 'sqlCreateFiles' is used (i.e. output
* from startSQLRepository is not being used) then the initializingGSA will always
* do the following at startup, unless property 'executeCreateAndDropScripts' is set to false:
* <pre>
* a) execute the appropriate dropSqlFile(s)
* b) execute the appropriate createSqlFile(s)
* </pre>
* If 'executeCreateAndDropScripts' is false then in the case where scripts normally would be run
* they will instead be skipped and no SQL (from scripts or startSQLRepository) will be executed.
* The reason for this restriction is that it's too difficult to know whether a database has
* been properly reset for the 'createSqlFile(s)' to run properly, so we err on the conservative
* side and always reset it.
*/
public void setSqlCreateFiles(Properties pFiles) {
mSqlCreateFiles = pFiles;
}
/** returns optional mapping of user-specified sql files that should be executed instead of
* the SQL generated by startSQLRepository. see 'setSqlCreateFiles' for detailed
* explanation of this property.
*/
public Properties getSqlCreateFiles() {
return mSqlCreateFiles;
}
private Properties mSqlDropFiles = new Properties();
/** returns optional mapping of user-specified sql files that should be executed during
* 'tear-down' instead of basing it on the SQL generated by startSQLRepository. see
* 'setSqlCreateFiles' for detailed explanation of this property.
*/
public void setSqlDropFiles(Properties pFiles) {
mSqlDropFiles = pFiles;
}
/** returns optional mapping of user-specified sql files that should be executed during
* 'tear-down' instead of basing it on the SQL generated by startSQLRepository. see
* 'setSqlCreateFiles' for detailed explanation of this property.
*/
public Properties getSqlDropFiles() {
return mSqlDropFiles;
}
private boolean mExecuteCreateDropScripts = true;
/** if set to true then create and drop scripts mapped through properties 'setSqlCreateFiles'
* and 'getSqlCreateFiles' will be executed. otherwise the scripts will not be executed at
* startup.
*/
public void setExecuteCreateAndDropScripts(boolean pExec) {
mExecuteCreateDropScripts = pExec;
}
/** returns true if create and drop scripts mapped through properties 'setSqlCreateFiles'
* and 'getSqlCreateFiles' should be executed at startup.
*/
public boolean isExecuteCreateAndDropScripts() {
return mExecuteCreateDropScripts;
}
private boolean mLoadColumnInfosAtInitialStartup = false;
/** returns true if the GSA should load JDBC metadata when starting the
* initial instantiation of the component. default: false
*/
public boolean isLoadColumnInfosAtInitialStartup() {
return mLoadColumnInfosAtInitialStartup;
}
/** set to true if the GSA should load JDBC metadata when starting the initial
* instantiation of the component. the default is false b/c the initial instantiation
* is only used to create tables and loading the metadata before the tables are
* created is unnecessary overhead which slows the startup process. When the
* component is restarted after the tables are created it uses the value of
* 'loadColumnInfosAtStartup' to determine whether to load the metadata on the restart. */
public void setLoadColumnInfosAtInitialStartup(boolean pLoad) {
mLoadColumnInfosAtInitialStartup = pLoad;
}
// ------------------------------------------------------------------------
// properties for version repository import
private String mProjectName = null;
public void setProjectName(String pProjectName) {
mProjectName = pProjectName;
}
public String getProjectName() {
return mProjectName;
}
private String mProjectType = "Standard";
public void setProjectType(String pProjectType) {
mProjectType = pProjectType;
}
public String getProjectType() {
return mProjectType;
}
private String mUser = null;
public void setUser(String pUser) {
mUser = pUser;
}
public String getUser() {
return mUser;
}
private String mWorkspaceIdPrefix = null;
public void setWorkspaceIdPrefix(String pWorkspaceIdPrefix) {
mWorkspaceIdPrefix = pWorkspaceIdPrefix;
}
public String getWorkspaceIdPrefix() {
return mWorkspaceIdPrefix;
}
private String mBranchId = null;
public void setBranchId(String pBranchId) {
mBranchId = pBranchId;
}
public String getBranchId() {
return mBranchId;
}
private String mComment = null;
public void setComment(String pComment) {
mComment = pComment;
}
public String getComment() {
return mComment;
}
private boolean mDoCheckin = true;
public void setDoCheckin(boolean pDoCheckin) {
mDoCheckin = pDoCheckin;
}
public boolean getDoCheckin() {
return mDoCheckin;
}
public boolean isDoCheckin() {
return mDoCheckin;
}
public boolean mRestartAfterTableCreation = true;
/**
* Returns true if this repository will attempt to
* "restart" after creating tables.
* @return
*/
public boolean isRestartingAfterTableCreation() {
return mRestartAfterTableCreation;
}
/**
* Sets if this repository will attempt to
* "restart" after creating tables.
* A value of true means that it should restart.
*/
public void setRestartingAfterTableCreation(boolean pRestart) {
mRestartAfterTableCreation = pRestart;
}
//-------------------------------------------------------------------------
// Member properties
// this property is a little tricky and a bit of a hack, but it
// allows us to create the tables, etc on startup. When the component
// is initially started this will be false, but when it calls restart,
// we set it to true for the new instantiation to avoid infinitely
// recursing into new repositories
private boolean mTemporaryInstantiation = false;
public void setIsTemporaryInstantiation(boolean pTemp) {
mTemporaryInstantiation = pTemp;
}
private boolean isTemporaryInstantiation() {
return mTemporaryInstantiation;
}
//-------------------------------------------------------------------------
// Methods
/** Overrides doStartService from VersionRepository to make the
* repository optionally create required tables by specified sql scripts and load data
* using the TemplateParser -import flag.
*
* @exception RepositoryException (?)
*
*/
public void doStartService() {
// if this is the temporary instantiation, we just want to
// call super.doStartService() and return
if (isTemporaryInstantiation()) {
if (isLoggingInfo())
logInfo("Restarting the Versioned GSA component to successfully load XML templates...");
super.doStartService();
return;
}
// also set 'loadColumnInfosAtStartup' to false to prevent attempts at
// loading lots of unwanted metadata. that's very time consuming and only needed
// by the final instantiation. The setLoadColumnInfosAtStartup method is new so
// use a try/catch in case we're dealing with an old version of GSARepository
boolean loadColumnInfosAtStartup = true;
try {
loadColumnInfosAtStartup = isLoadColumnInfosAtStartup();
setLoadColumnInfosAtStartup(isLoadColumnInfosAtInitialStartup());
if (isLoadColumnInfosAtInitialStartup()) {
if (isLoggingInfo())
logInfo("Enabled loading of column info for initial startup");
} else {
if (isLoggingInfo())
logInfo("Disabled loading of column info for initial startup");
}
} catch (Throwable t) {
if (isLoggingDebug())
logDebug("Could not modify loading of column metadata for preliminary startup.");
}
setLoggingWarning(false);
setLoggingError(true);
// call GSA.doStartService to load XML definition files
super.doStartService();
setLoggingError(true);
setLoggingWarning(true);
// reset 'LoadColumnInfosAtStartup' to whatever it was originally
try {
setLoadColumnInfosAtStartup(loadColumnInfosAtStartup);
} catch (Throwable t) {
}
try {
// now create the tables and restart the repository
// setLoggingDebug(true);
boolean createdTables = createTables();
// boolean old = isDropTablesAtShutdown();
// setDropTablesAtShutdown(false);
// super.doStopService();
// setDropTablesAtShutdown(old);
// super.doStartService();
if (isRestartingAfterTableCreation()) {
restart();
}
// we're now ready to import specified XML files
if (isImportEveryStartup() || createdTables)
importFiles();
else {
if (isLoggingInfo())
logInfo("Import not performed because importEveryStartup is false and no tables were created.");
}
} catch (Exception e) {
logError(
"Caught an unexpected exception trying to create tables or importFiles ...",
e);
}
}
//-----------------------------------------
/**
* Restarts the repository. This involves re-reading nucleus properties,
* reloading definition files, and invalidating all cache entries. This method
* is a convenience for development purposes (to avoid restarting dynamo when
* a template has changed), and should not be used on a live site.
*
* This method is modified slightly from the restart method of GSARepository
* because it sets mTemporaryInstantiation to true so that the doStartService
* method of the new instance does not reload import files or try to recreate
* tables
*/
public boolean restart() throws ServiceException {
Configuration c = getServiceConfiguration();
NucleusNameResolver r = new NucleusNameResolver(getNucleus(), getNucleus(),
getNameContext(), true);
InitializingVersionRepository newRepository = (InitializingVersionRepository) c.createNewInstance(this);
c.configureService(newRepository, r, this);
// Fool this new repository into thinking that it has been
// bound to the same name context as the original repository
// This changes will make sure that getAbsoluteName() returns
// a correct value.
((GenericService) this).getNameContext();
new NameContextBindingEvent(this.getName()+"_ver", getWrappedRepository(), this.getNameContext());
// newRepository.nameContextElementBound(bindingEvent);
// nc.removeElement(this.getName()+"_ver");
// super.setWrappedRepository(null);
ServiceEvent ev = new ServiceEvent(this, getWrappedRepository(), getNucleus(), c);
/*
* We are purposefully not putting the new repository into the parent's name
* context. The existing repository is always the valid one. We're starting
* this new guy, then we're going to synchronize on the repository and get
* all of its info into us.
*/
// we have to set the new repository as temporary so it won't call
// restart and start an infinite recursion
newRepository.setIsTemporaryInstantiation(true);
getWrappedRepository().startService(ev);
// bindingEvent = new NameContextBindingEvent(this
// .getName()+"_ver", newRepository, this.getNameContext());
// newRepository.getWrappedRepository().nameContextElementUnbound(bindingEvent);
if (newRepository.isRunning()) {
synchronized (this) {
invalidateCaches();
copyFromOtherRepository(newRepository);
}
return true;
} else
return false;
}
/** This method is called when the repository is shutdown. If dropTablesAtShutdown
* is true, it will attempt to drop all the tables.
* IMPORTANT: There is an optional property that can be set to indicate that all tables
* should be dropped at shutdown (dropTablesAtShutdown). Because of the order in which
* Nucleus shuts down the components, this may or may not work. It just depends on whether
* the datasource is shutdown before the repository. If you want to guarantee that
* your tables are dropped, manually invoke the doStopService method from the HTML admin
* pages.
*/
public void doStopService() {
try {
if (isDropTablesAtShutdown()) {
if (isLoggingInfo())
logInfo("Dropping tables because 'dropTablesAtShutdown' is true....");
dropTables();
}
} catch (Exception e) {
if (isLoggingError())
logError(e);
} finally {
super.doStopService();
}
}
/** This method drops all tables required by the GSARepository.
*
* @exception RepositoryException if an error occurs while retrieving a
* list of the tables associated with the repository
* @exception SQLProcessorException if an error occured trying to
* drop the tables
*/
public void dropTables() throws RepositoryException, SQLProcessorException {
// execute SQL files, if specified
String[] dropFiles = getSpecifiedDropFiles();
if (dropFiles != null) {
if (isExecuteCreateAndDropScripts())
executeSqlFiles(dropFiles, false);
else if (isLoggingInfo())
logInfo("Skipping execution of SQL scripts b/c property 'executeCreateAndDropScripts' is false.");
return;
}
// otherwise, just drop tables based on startSQLRepository SQL -- not implement yet.
if (isLoggingInfo())
logInfo("Can not drop tables based on startSQLRepositoryRepository SQL. Please specified DropFiles!");
return;
}
/** This method creates the tables required by the GSARepository.
* If desired, check to make sure all the tables exist in the
* database. If a table doesn't exist, create it;
* if it does exist, don't do anything to it unless user wants
* to drop existing tables
*
* @return boolean - true if tables were created
* @exception RepositoryException if an error occurs while retrieving a list of the tables
* to create
* @exception SQLProcessorException if an error occured trying to
* create the tables
*/
private boolean createTables() throws RepositoryException,
SQLProcessorException {
// execute SQL files, if specified
String[] createFiles = getSpecifiedCreateFiles();
if (createFiles != null) {
if (!isExecuteCreateAndDropScripts()) {
if (isLoggingError())
logError("Skipping execution of SQL scripts b/c property 'executeCreateAndDropScripts' is false.");
return false;
}
// before executing the createFiles we always execute the drop files
String[] dropFiles = getSpecifiedDropFiles();
executeSqlFiles(dropFiles, false);
log.info(createFiles);
executeSqlFiles(createFiles, true);
return true;
}
// otherwise, just execute sql from startSQLRepository
boolean createdTables = false;
if (isCreateTables()) {
SQLProcessorEngine spe = getSQLProcessor();
// turn on debug for SQLProcessorEngine if GSA has debug on
if (isLoggingDebug())
spe.setLoggingDebug(true);
List<String> createStatements = getCreateStatements(null, null);
createdTables = spe.createTables(createStatements, isDropTablesIfExist());
}
return createdTables;
}
/**
* This method is used to retrieve all of the CREATE TABLE statements that are
* needed to generate tables for this GSA
*
* @exception RepositoryException
* if an error occurs with the Repository
*/
private List<String> getCreateStatements(PrintWriter pOut, String pDatabaseName)
throws RepositoryException {
List<String> tableStatements = new ArrayList<String>();
List<String> indexStatements = new ArrayList<String>();
// use current database if none is supplied
if (pDatabaseName == null)
pDatabaseName = getDatabaseName();
String[] descriptorNames = getWrappedRepository().getItemDescriptorNames();
OutputSQLContext sqlContext = new OutputSQLContext(pOut);
GSAItemDescriptor itemDescriptors[];
//DatabaseTableInfo dti = getDatabaseTableInfo(pDatabaseName);
int i, length = descriptorNames.length;
itemDescriptors = new GSAItemDescriptor[length];
for (i = 0; i < length; i++) {
itemDescriptors[i] = (GSAItemDescriptor) getWrappedRepository()
.getItemDescriptor(descriptorNames[i]);
}
String create = null;
String index = null;
HashSet<String> tableNames = new HashSet<String>();
for (i = 0; i < length; i++) {
GSAItemDescriptor desc = itemDescriptors[i];
Table[] tables = desc.getTables();
if (tables != null) {
for (int j = 0; j < tables.length; j++) {
Table t = tables[j];
if (!t.isInherited() && !tableNames.contains(t.getName())) {
sqlContext.clear();
create = t.generateSQL(sqlContext, pDatabaseName);
// get rid of any possible CREATE INDEX statements and store those
// in their own Vector of statements...
index = extractIndexStatement(create);
create = removeIndexStatements(create);
if (isStripReferences())
create = stripReferences(create);
if (index != null && !indexStatements.contains(index))
indexStatements.add(index);
if (create != null && !tableStatements.contains(create))
tableStatements.add(create);
tableNames.add(t.getName());
}
}
}
}
/*
* if (pOut != null) { pOut.print(buffer); pOut.flush(); }
*/
return tableStatements;
}
/** This method imports files using the TemplateParser
*
* @exception RepositoryException if an error occured while importing
* one of the xml files.
*/
private void importFiles() throws RepositoryException {
if (isLoggingInfo())
logInfo("Importing files...");
String[] loadFiles = getImportFilesAsStrings();
// just exit if no files were specified
if (loadFiles == null) {
if (isLoggingInfo())
logInfo("No files specified for import.");
return;
}
if (isLoggingDebug()) {
logDebug("The following files will be imported:");
for (int i = 0; i < loadFiles.length; i++) {
logDebug("file: " + loadFiles[i]);
}
}
// now load the import files if they were specified
PrintWriter ps = new PrintWriter(System.out);
if (loadFiles != null && loadFiles.length > 0) {
try {
String pProjectName = getProjectName();
String pProjectType = getProjectType();
String pUser = getUser();
String pWorkspaceId = getWorkspaceIdPrefix();
String pBranchId = getBranchId();
String pComment = getComment();
boolean pDoCheckin = isDoCheckin();
// check the versioning flags for correctness
if (isLoggingDebug())
logDebug("checking the versioning flags for correctness ... ");
if (isLoggingDebug())
logDebug("pProjectName = " + pProjectName);
if (isLoggingDebug())
logDebug("pProjectType = " + pProjectType);
if (isLoggingDebug())
logDebug("pUser = " + pUser);
if (isLoggingDebug())
logDebug("pWrokspaceId = " + pWorkspaceId);
if (isLoggingDebug())
logDebug("pBranchId = " + pBranchId);
if (isLoggingDebug())
logDebug("pComment = " + pComment);
if (isLoggingDebug())
logDebug("pDoCheckin = " + pDoCheckin);
if (pProjectName == null && (pWorkspaceId == null || pBranchId == null)) {
if (pWorkspaceId == null) {
if (isLoggingError())
logError("Error: workspaceId required for a versioned import");
} else {
if (isLoggingError())
logError("Error: branchId required for a versioned import");
}
return;
} else if (pProjectName == null
&& (pDoCheckin == true && pComment == null)) {
if (isLoggingError())
logError("Error: comment required for a versioned import");
return;
} else if (pProjectName != null && pUser == null) {
if (isLoggingError())
logError("Error: user required for a versioned import");
return;
} else if (pProjectName != null && pDoCheckin == true
&& pComment == null) {
if (isLoggingError())
logError("Error: comment required for a versioned import");
return;
}
//do importFiles
if (pProjectName != null) {
// If porjectName is supplied, we will use projectName, projectType, user to create a project and get its workspaceId.
TemplateParser.importFiles(this, loadFiles, ps,
isImportWithTransaction(), VersioningContextUtil
.versioningContextHelper(pProjectName, pProjectType, pUser,
pWorkspaceId, pBranchId, pComment, pDoCheckin));
} else {
// if workspaceId is supplied, we will add a random number after workspaceId to avoid duplicate.
TemplateParser.importFiles(this, loadFiles, ps,
isImportWithTransaction(), VersioningContextUtil
.versioningContextHelper(pWorkspaceId + StrictMath.random()
* 10, pBranchId, pComment, pDoCheckin));
}
} catch (Exception e) {
throw new RepositoryException(
"Exception caught importing files into repository.", e);
}
}
}
/**
* This method is used to remove the 'references...' parts from
* sql generated by the GSA. Removing the references allows us to
* avoid problems of creating tables in the wrong order and also
* allows you to easily drop / recreate tables.
*/
private String stripReferences(String pStr) {
if (isLoggingDebug()) {
logDebug("Removing references from SQL string...");
if (this.getDebugLevel() > 6)
logDebug("SQL string before references are removed: \n" + pStr);
}
pStr = stripForeignKey(pStr);
// must be of the following format
// fieldname data-type null references foo(id),
String ref = "references ";
String endRef = ",";
StringBuffer sb = new StringBuffer();
int start = 0;
int end = 0;
end = pStr.indexOf(ref);
while (end != -1) {
String temp = pStr.substring(start, end);
sb.append(temp);
pStr = pStr.substring(end + ref.length());
start = pStr.indexOf(endRef);
end = pStr.indexOf(ref);
}
String temp2 = pStr.substring(start);
sb.append(temp2);
if (isLoggingDebug())
logDebug("Final sql string -> references removed: \n" + sb.toString());
return sb.toString();
}
private String stripForeignKey(String pStr) {
if (isLoggingDebug()) {
logDebug("Removing Foreign Key from SQL string...");
if (this.getDebugLevel() > 6)
logDebug("SQL string before Foreign Key are removed: \n" + pStr);
}
String key = "foreign key";
int flag = 0;
int end = 0;
end = pStr.toLowerCase().lastIndexOf(key);
while (end != -1) {
flag = 1;
pStr = pStr.substring(0, end);
end = pStr.toLowerCase().lastIndexOf(key);
}
end = pStr.lastIndexOf(",");
if (flag == 0)
return pStr;
else
return pStr.substring(0, end) + " )";
}
/** This method is used to extract a possible CREATE INDEX statement from
* a CREATE TABLE statement that is generated by a Table. If no CREATE
* INDEX statement is included, it returns null
*/
private String extractIndexStatement(String pStatement) {
String search = "CREATE INDEX ";
String copy = pStatement.toUpperCase();
int i = copy.indexOf(search);
if (i != -1)
return stripTrailingSemiColon(pStatement.substring(i));
return null;
}
/** This method is used to remove any possible CREATE INDEX statements from
* the end of a CREATE TABLE statement generated by a Table. It returns the
* CREATE TABLE statement with all CREATE INDEX statements removed.
*/
private String removeIndexStatements(String pStatement) {
String search = "CREATE INDEX ";
String copy = pStatement.toUpperCase();
int i = copy.indexOf(search);
if (i != -1)
pStatement = pStatement.substring(0, i);
return stripTrailingSemiColon(pStatement);
}
/** This method is used to remove the trailing semicolon from a String. It is assumed
* that these strings will only possibly have one semicolon, and that if there is one
* everything after the semicolon is junk.
*/
private String stripTrailingSemiColon(String pStr) {
if (pStr == null)
return pStr;
int idx = pStr.indexOf(";");
if (idx != -1)
pStr = pStr.substring(0, idx);
return pStr;
}
// ---------- methods to help with user-specified SQL files -----------
// allowable db types to specify
public String SOLID = "solid";
public String ORACLE = "oracle";
public String MICROSOFT = "microsoft";
public String INFORMIX = "informix";
public String DB2 = "db2";
public String SYBASE = "sybase";
public String SYBASE2 = "Adaptive Server Enterprise"; // sybase 12.5
public String DEFAULT = "default";
private String[] dbTypes = { SOLID, ORACLE, MICROSOFT, INFORMIX, DB2, SYBASE,
SYBASE2, DEFAULT };
/** returns the dbtype for the database being used. returned value will be one
* of the constants SOLID, ORACLE, MICROSOFT, INFORMIX, DB2, SYBASE, or DEFAULT
* if db type can not be determined.
*/
private String getDatabaseType() {
String type = getDatabaseName();
for (int i = 0; i < dbTypes.length; i++) {
if (type.toLowerCase().indexOf(dbTypes[i].toLowerCase()) > -1) {
if (dbTypes[i].equals(SYBASE2))
return SYBASE;
return dbTypes[i];
}
}
return DEFAULT;
}
/** returns array of user-specified SQL files that should be executed, or null
* if output from startSQLRepository should be used.
* @exception RepositoryException if an error occurs getting the array of files to execute
*/
private String[] getSpecifiedCreateFiles() throws RepositoryException {
// try to get mapped value for this specific db type, and if it's empty try the default
String files = (String) getSqlCreateFiles().get(getDatabaseType());
if (files == null)
files = (String) getSqlCreateFiles().get(DEFAULT);
// if it's still empty then just return b/c there's nothing to execute
if (files == null)
return null;
// if file list is not null, convert it and return the array
try {
return TestUtils.convertFileArray(files, ",");
} catch (Exception e) {
throw new RepositoryException(e);
}
}
/** returns array of user-specified SQL files that should be executed, or null
* if output from startSQLRepository should be used.
* @exception RepositoryException if an error occurs getting the array of files to execute
*/
private String[] getSpecifiedDropFiles() throws RepositoryException {
// try to get mapped value for this specific db type, and if it's empty try the default
String files = (String) getSqlDropFiles().get(getDatabaseType());
if (files == null)
files = (String) getSqlDropFiles().get(DEFAULT);
// if it's still empty then just return b/c there's nothing to execute
if (files == null)
return null;
// if file list is not null, convert it and return the array
try {
return TestUtils.convertFileArray(files, ",");
} catch (Exception e) {
throw new RepositoryException(e);
}
}
// /** verifies that SQL files specified by user are ok. in particular, that if
// * the user mapped a 'createSqlFile' for a db type there is a corresponding
// * 'dropSqlFile' entry, and vice-versa.
// * @exception RepositoryException if anything is wrong
// */
// private void validateUserSpecifiedSqlFiles() throws RepositoryException {
// // don't let them be null
// if (getSqlCreateFiles() == null)
// setSqlCreateFiles(new Properties());
// if (getSqlDropFiles() == null)
// setSqlDropFiles(new Properties());
// // make sure all the keys are valid
// Set<Object> keys = new HashSet<Object>();
// keys.addAll(getSqlCreateFiles().keySet());
// keys.addAll(getSqlDropFiles().keySet());
// Set<String> allow_keys = new HashSet<String>();
// for (int i = 0; i < dbTypes.length; i++) {
// keys.remove(dbTypes[i]);
// if (!dbTypes[i].equals(SYBASE2))
// allow_keys.add(dbTypes[i]);
// }
// if (keys.size() > 0)
// throw new RepositoryException(
// "The following keys used in the 'sqlCreateFiles' and/or 'sqlDropFiles' properties "
// + "are invalid: " + keys + ". Allowable keys are: " + allow_keys);
//
// boolean isDefaultCreate = (getSqlCreateFiles().get(DEFAULT) != null);
// boolean isDefaultDrop = (getSqlDropFiles().get(DEFAULT) != null);
// // if there are defaults it will always be ok, so just return
// if (isDefaultCreate && isDefaultDrop)
// return;
//
// // otherwise, check each dbType individually
// for (int i = 0; i < dbTypes.length; i++) {
// boolean isCreate = (getSqlCreateFiles().get(dbTypes[i]) != null);
// boolean isDrop = (getSqlDropFiles().get(dbTypes[i]) != null);
// if (isCreate && !isDrop && !isDefaultDrop)
// throw new RepositoryException(
// "Mapping exists for database type "
// + dbTypes[i]
// + " in property 'sqlCreateFiles', but not in property 'sqlDropFiles', and "
// + "there is no default specified.");
// if (isDrop && !isCreate && !isDefaultCreate)
// throw new RepositoryException(
// "Mapping exists for database type "
// + dbTypes[i]
// + " in property 'sqlDropFiles', but not in property 'sqlCreateFiles', and "
// + "there is no default specified.");
// }
// }
/** executes the specified SQL files against this Repository's DataSource.
* @param String[] the files to execute
* @param boolean true if execution should stop at first error. if false, then a warning
* will be printed for encountered errors.
* @exception RepositoryException if pStopAtError is true and an error occurs while
* executing one of the sql statements.
*/
private void executeSqlFiles(String[] pFiles, boolean pStopAtError)
throws RepositoryException {
SQLProcessor sp = new SQLProcessor(getTransactionManager(), getDataSource());
// for sql server auto-commit must be true
// if ( getDatabaseType().equals( MICROSOFT ) ) sp.setAutoCommit(true);
SQLFileParser parser = new SQLFileParser();
for (int i = 0; i < pFiles.length; i++) {
String file = pFiles[i];
// switch the file path so everything is forward slashes
file = file.replace('\\', '/');
String cmd = null;
Iterator<?> cmds = null;
if (isLoggingInfo())
logInfo("Executing SQL file: " + file);
if (!new File(file).exists())
throw new RepositoryException("SQL file " + file + " does not exist.");
// parse the file to get commands...
try {
Collection<?> c = parser.parseSQLFile(file);
if (isLoggingDebug())
logDebug("Parsed " + c.size() + " SQL command(s) from file.");
cmds = c.iterator();
} catch (Exception e) {
// an error parsing the file indicates something very wrong, so bail
throw new RepositoryException("Error encountered parsing SQL file "
+ file, e);
}
// then execute the commands...
while (cmds.hasNext()) {
cmd = (String) cmds.next();
if (isLoggingDebug() || isLoggingCreateTables())
logDebug("Executing SQL cmd [" + cmd + "]");
try {
sp.executeSQL(cmd);
} catch (Exception e) {
if (pStopAtError) {
throw new RepositoryException("Error received executing command ["
+ cmd + "] from SQL file " + file, e);
} else {
if (isLoggingWarning())
logWarning("Error received executing command [" + cmd
+ "] from SQL file " + file + ": " + e.getMessage());
}
}
}
}
}
} // end of class