From 5b6ff13d94f415fa6365937e746a424a9e8ef4a3 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Thu, 11 Jun 2009 10:30:57 +0000 Subject: [PATCH] Merged V3.1 to HEAD 14192: Support for variable assignment and replacement in upgrade scripts 14263 (RECORD ONLY) 14286: Merged V2.2 to V3.1 14124: Fixed ETWOTWO-961: FileFolderService.listFolders throws UnsupportedOperationException for AVM nodes 14277: Fixed ETWOTWO-1228: CLONE -NodeService properties don't support collections of collections for d:any 14302: Merged DEV/V3.1_UPGRADE_SCRIPTS_2 to V3.1: DB2 scripts and other minor changes 14126 (RECORD ONLY) 14193 (RECORD ONLY) 14205: Next version of DB upgrade scripts for SQLServer and DB2 14208: Enterprise DB scripts review: Move scripts to correct locations as a first pass 14211: Carried V2.1-A script change into branch; use STR() function for numeric to string comparison 14212: Moved script from old SQLServer dialect directory 14213: Minor script formatting 14303: Merged DEV/V3.1_UPGRADE_SCRIPTS_2 to V3.1: DB2 scripts and other formatting 14214: Removed redundant scripts; these are all covered by generic scripts 14236: Format SQL 14248: Formatting of SQL to produce meaningful diffs 14266: Next version of upgrade scripts 14281: Clean up formatting of SQL scripts using Convert utility ___________________________________________________________________ Modified: svn:mergeinfo Merged /alfresco/BRANCHES/DEV/V3.1_UPGRADE_SCRIPTS_2:r14126,14193,14205,14208,14211-14213 Merged /alfresco/BRANCHES/V2.2:r14124,14130,14277 Merged /alfresco/BRANCHES/V3.1:r14192,14263,14286,14302-14303 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14652 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../AlfrescoSchemaUpdate-Person.sql | 6 +- .../messages/schema-update.properties | 2 + .../org/alfresco/repo/avm/AVMNodeService.java | 56 +++++++++++++++- .../repo/domain/NodePropertyValue.java | 23 ------- .../repo/domain/schema/SchemaBootstrap.java | 66 ++++++++++++++++++- .../repo/node/BaseNodeServiceTest.java | 60 ++++++++++++++++- .../HibernateNodeDaoServiceImpl.java | 11 ++-- 7 files changed, 190 insertions(+), 34 deletions(-) diff --git a/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.Dialect/AlfrescoSchemaUpdate-Person.sql b/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.Dialect/AlfrescoSchemaUpdate-Person.sql index 33f700d8e4..4e81768fc4 100644 --- a/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.Dialect/AlfrescoSchemaUpdate-Person.sql +++ b/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.Dialect/AlfrescoSchemaUpdate-Person.sql @@ -18,7 +18,8 @@ UPDATE FROM alf_namespace n WHERE - n.uri = 'http://www.alfresco.org/model/content/1.0'), + n.uri = 'http://www.alfresco.org/model/content/1.0' + ), qname_localname = ( SELECT @@ -29,7 +30,8 @@ UPDATE JOIN alf_namespace n on q.ns_id = n.id WHERE p.node_id = alf_child_assoc.child_node_id AND - q.local_name ='userName' AND n.uri = 'http://www.alfresco.org/model/content/1.0' + q.local_name ='userName' AND + n.uri = 'http://www.alfresco.org/model/content/1.0' ) WHERE exists ( diff --git a/config/alfresco/messages/schema-update.properties b/config/alfresco/messages/schema-update.properties index 0521703f2e..2da9977e0a 100644 --- a/config/alfresco/messages/schema-update.properties +++ b/config/alfresco/messages/schema-update.properties @@ -24,4 +24,6 @@ schema.update.err.update_failed=Schema auto-update failed schema.update.err.validation_failed=Schema validation failed schema.update.err.update_script_not_run=The following schema upgrade script needs to be executed manually: {0} schema.update.err.script_not_found=The schema script could not be found at location {0} +schema.update.err.statement_var_assignment_before_sql=Variable assignment with '--ASSIGN:' must occur before starting the SQL statement (line {0} of {1}). +schema.update.err.statement_var_assignment_format=Variable assignment uses format '--ASSIGN:x=col' where 'x' is the variable to assign to and 'col' is the column value to extract (line {0} of {1}). schema.update.err.statement_terminator=Scripts must terminate all statements with ';' (line {0} of {1}). diff --git a/source/java/org/alfresco/repo/avm/AVMNodeService.java b/source/java/org/alfresco/repo/avm/AVMNodeService.java index a4c2ddeef0..b5b3fd17ff 100644 --- a/source/java/org/alfresco/repo/avm/AVMNodeService.java +++ b/source/java/org/alfresco/repo/avm/AVMNodeService.java @@ -1614,7 +1614,61 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi public List getChildAssocs(NodeRef nodeRef, Set childNodeTypes) { - throw new UnsupportedOperationException(); + /* + * ETWOTWO-961 forced an implementation, but this is just a workaround. + * We do a listing and then keep files or folders looking specifically + * for cm:folder and cm:content types from childNodeTypes. + */ + Pair avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + int version = avmVersionPath.getFirst(); + String path = avmVersionPath.getSecond(); + List result = new ArrayList(); + SortedMap children = null; + try + { + children = + fAVMService.getDirectoryListing(version, + path); + } + catch (AVMNotFoundException e) + { + return result; + } + for (Map.Entry entry : children.entrySet()) + { + String name = entry.getKey(); + AVMNodeDescriptor descriptor = entry.getValue(); + if (descriptor.isFile()) + { + if (!childNodeTypes.contains(ContentModel.TYPE_CONTENT)) + { + continue; + } + } + else if (descriptor.isDirectory()) + { + if (!childNodeTypes.contains(ContentModel.TYPE_FOLDER)) + { + continue; + } + } + else + { + // Not a file or directory??? + continue; + } + result.add(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, + nodeRef, + QName.createQName( + NamespaceService.CONTENT_MODEL_1_0_URI, + name), + AVMNodeConverter.ToNodeRef( + version, + AVMNodeConverter.ExtendAVMPath(path, name)), + true, + -1)); + } + return result; } public List getChildrenByName(NodeRef nodeRef, QName assocTypeQName, Collection childNames) diff --git a/source/java/org/alfresco/repo/domain/NodePropertyValue.java b/source/java/org/alfresco/repo/domain/NodePropertyValue.java index d75e3b707c..f1860d51d3 100644 --- a/source/java/org/alfresco/repo/domain/NodePropertyValue.java +++ b/source/java/org/alfresco/repo/domain/NodePropertyValue.java @@ -423,21 +423,6 @@ public class NodePropertyValue implements Cloneable, Serializable return Integer.valueOf(19); } - @SuppressWarnings("unchecked") - @Override - protected ValueType getPersistedType(Serializable value) - { - if (value instanceof Collection) - { - Collection collectionValue = (Collection) value; - if (collectionValue.size() == 0) - { - return ValueType.NULL; - } - } - return ValueType.SERIALIZABLE; - } - /** * @return Returns and empty Collection if the value is null * otherwise it just returns the original value @@ -569,14 +554,6 @@ public class NodePropertyValue implements Cloneable, Serializable { return ValueType.VERSION_NUMBER; } - else if (value instanceof Collection) - { - return ValueType.COLLECTION; - } - else if (value instanceof MLText) - { - return ValueType.MLTEXT; - } else if (value instanceof Period) { return ValueType.PERIOD; diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index 7850a4e1c8..4ff1e64bc5 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -39,7 +39,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import org.alfresco.error.AlfrescoRuntimeException; @@ -125,6 +127,8 @@ public class SchemaBootstrap extends AbstractLifecycleBean private static final String ERR_VALIDATION_FAILED = "schema.update.err.validation_failed"; private static final String ERR_SCRIPT_NOT_RUN = "schema.update.err.update_script_not_run"; private static final String ERR_SCRIPT_NOT_FOUND = "schema.update.err.script_not_found"; + private static final String ERR_STATEMENT_VAR_ASSIGNMENT_BEFORE_SQL = "schema.update.err.statement_var_assignment_before_sql"; + private static final String ERR_STATEMENT_VAR_ASSIGNMENT_FORMAT = "schema.update.err.statement_var_assignment_format"; private static final String ERR_STATEMENT_TERMINATOR = "schema.update.err.statement_terminator"; public static final int DEFAULT_LOCK_RETRY_COUNT = 24; @@ -910,6 +914,9 @@ public class SchemaBootstrap extends AbstractLifecycleBean int line = 0; // loop through all statements StringBuilder sb = new StringBuilder(1024); + String fetchVarName = null; + String fetchColumnName = null; + Map varAssignments = new HashMap(13); while(true) { String sqlOriginal = reader.readLine(); @@ -923,6 +930,25 @@ public class SchemaBootstrap extends AbstractLifecycleBean // trim it String sql = sqlOriginal.trim(); + // Check for variable assignment + if (sql.startsWith("--ASSIGN:")) + { + if (sb.length() > 0) + { + // This can only be set before a new SQL statement + throw AlfrescoRuntimeException.create(ERR_STATEMENT_VAR_ASSIGNMENT_BEFORE_SQL, (line - 1), scriptUrl); + } + String assignStr = sql.substring(9, sql.length()); + String[] assigns = assignStr.split("="); + if (assigns.length != 2 || assigns[0].length() == 0 || assigns[1].length() == 0) + { + throw AlfrescoRuntimeException.create(ERR_STATEMENT_VAR_ASSIGNMENT_FORMAT, (line - 1), scriptUrl); + } + fetchVarName = assigns[0]; + fetchColumnName = assigns[1]; + continue; + } + // Check for comments if (sql.length() == 0 || sql.startsWith( "--" ) || sql.startsWith( "//" ) || @@ -978,8 +1004,23 @@ public class SchemaBootstrap extends AbstractLifecycleBean if (execute) { sql = sb.toString(); - executeStatement(connection, sql, optional, line, scriptFile); + + // Perform variable replacement using the ${var} format + for (Map.Entry entry : varAssignments.entrySet()) + { + String var = entry.getKey(); + Object val = entry.getValue(); + sql = sql.replaceAll("\\$\\{" + var + "\\}", val.toString()); + } + + Object fetchedVal = executeStatement(connection, sql, fetchColumnName, optional, line, scriptFile); + if (fetchVarName != null && fetchColumnName != null) + { + varAssignments.put(fetchVarName, fetchedVal); + } sb = new StringBuilder(1024); + fetchVarName = null; + fetchColumnName = null; } } } @@ -993,8 +1034,16 @@ public class SchemaBootstrap extends AbstractLifecycleBean /** * Execute the given SQL statement, absorbing exceptions that we expect during * schema creation or upgrade. + * + * @param fetchColumnName the name of the column value to return */ - private void executeStatement(Connection connection, String sql, boolean optional, int line, File file) throws Exception + private Object executeStatement( + Connection connection, + String sql, + String fetchColumnName, + boolean optional, + int line, + File file) throws Exception { StringBuilder executedStatements = executedStatementsThreadLocal.get(); if (executedStatements == null) @@ -1003,15 +1052,25 @@ public class SchemaBootstrap extends AbstractLifecycleBean } Statement stmt = connection.createStatement(); + Object ret = null; try { if (logger.isDebugEnabled()) { LogUtil.debug(logger, MSG_EXECUTING_STATEMENT, sql); } - stmt.execute(sql); + boolean haveResults = stmt.execute(sql); // Record the statement executedStatements.append(sql).append(";\n\n"); + if (haveResults && fetchColumnName != null) + { + ResultSet rs = stmt.getResultSet(); + if (rs.next()) + { + // Get the result value + ret = rs.getObject(fetchColumnName); + } + } } catch (SQLException e) { @@ -1030,6 +1089,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean { try { stmt.close(); } catch (Throwable e) {} } + return ret; } /** diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index d395aa201e..57ab85a9f9 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -1430,6 +1431,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest assertTrue("Serialization/deserialization failed", checkPropertyQname instanceof QName); } + @SuppressWarnings("unchecked") public void testMultiProp() throws Exception { QName undeclaredPropQName = QName.createQName(NAMESPACE, getName()); @@ -1439,7 +1441,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName("pathA"), TYPE_QNAME_TEST_MULTIPLE_TESTER).getChildRef(); - ArrayList values = new ArrayList(1); + ArrayList values = new ArrayList(1); values.add("ABC"); values.add("DEF"); // test allowable conditions @@ -1473,6 +1475,62 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest { try { txn.rollback(); } catch (Throwable e) {} } + + txn = transactionService.getUserTransaction(); + try + { + txn.begin(); + // Check that multi-valued d:mltext can be collections of MLText + values.clear(); + values.add(new MLText("ABC")); + values.add(new MLText("DEF")); + nodeService.setProperty(nodeRef, PROP_QNAME_MULTI_ML_VALUE, values); + List checkValues = (List) nodeService.getProperty( + nodeRef, PROP_QNAME_MULTI_ML_VALUE); + assertEquals("Expected 2 MLText values back", 2, checkValues.size()); + assertTrue("Incorrect type in collection", checkValues.get(0) instanceof MLText); + assertTrue("Incorrect type in collection", checkValues.get(1) instanceof MLText); + + // Check that multi-valued d:any properties can be collections of collections (empty) + // We put ArrayLists and HashSets into the Collection of d:any, so that is exactly what should come out + values.clear(); + ArrayList arrayListVal = new ArrayList(2); + HashSet hashSetVal = new HashSet(2); + values.add(arrayListVal); + values.add(hashSetVal); + nodeService.setProperty(nodeRef, PROP_QNAME_ANY_PROP_MULTIPLE, values); + checkValues = (List) nodeService.getProperty( + nodeRef, PROP_QNAME_ANY_PROP_MULTIPLE); + assertEquals("Expected 2 Collection values back", 2, checkValues.size()); + assertTrue("Incorrect type in collection", checkValues.get(0) instanceof ArrayList); // ArrayList in - ArrayList out + assertTrue("Incorrect type in collection", checkValues.get(1) instanceof HashSet); // HashSet in - HashSet out + + // Check that multi-valued d:any properties can be collections of collections (with values) + // We put ArrayLists and HashSets into the Collection of d:any, so that is exactly what should come out + arrayListVal.add("ONE"); + arrayListVal.add("TWO"); + hashSetVal.add("ONE"); + hashSetVal.add("TWO"); + values.clear(); + values.add(arrayListVal); + values.add(hashSetVal); + nodeService.setProperty(nodeRef, PROP_QNAME_ANY_PROP_MULTIPLE, values); + checkValues = (List) nodeService.getProperty( + nodeRef, PROP_QNAME_ANY_PROP_MULTIPLE); + assertEquals("Expected 2 Collection values back", 2, checkValues.size()); + assertTrue("Incorrect type in collection", checkValues.get(0) instanceof ArrayList); // ArrayList in - ArrayList out + assertTrue("Incorrect type in collection", checkValues.get(1) instanceof HashSet); // HashSet in - HashSet out + assertEquals("First collection incorrect", 2, ((Collection)checkValues.get(0)).size()); + assertEquals("Second collection incorrect", 2, ((Collection)checkValues.get(1)).size()); + } + catch (DictionaryException e) + { + // expected + } + finally + { + try { txn.rollback(); } catch (Throwable e) {} + } } /** diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java index 7b568a6b3e..00c6d680fb 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -4425,6 +4425,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements LocaleDAO localeDAO) { Serializable result = null; + Collection collectionResult = null; // A working map. Ordering is not important for this map. Map scratch = new HashMap(3); // Iterate (sorted) over the map entries and extract values with the same list index @@ -4456,15 +4457,17 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { result = collapsedValue; } - else if (result instanceof Collection) + else if (collectionResult != null) { - @SuppressWarnings("unchecked") - Collection collectionResult = (Collection) result; + // We have started a collection, so just add the value to it. collectionResult.add(collapsedValue); } else { - Collection collectionResult = new ArrayList(20); + // We already had a result, and now have another. A collection has not been + // started. We start a collection and explicitly keep track of it so that + // we don't get mixed up with collections of collections (ETHREEOH-2064). + collectionResult = new ArrayList(20); collectionResult.add(result); // Add the first result collectionResult.add(collapsedValue); // Add the new value result = (Serializable) collectionResult;