From 72a90a14f1d28519376a9c43c6aec2ee2d9533cc Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Mon, 10 Mar 2008 12:17:10 +0000 Subject: [PATCH] Merged V2.2 to HEAD 8078: Merged V2.1 to V2.2 8025: Fixes WCM-1039, problems with case insensitive name handling. 8079: Merged V2.1 to V2.2 8035: -- DONE SEPARATELY -- 8040: Fix AR-1985: SQL Server dialect is derived from Sybase dialect there need additional no-op script 8046: Better Javadocs for getChildByName() 8056: Fixed WCM-790: Date conversion for metadata extractors 8057: Fixed WCM-790: Properties that don't convert can be discarded (default is to fail) 8059: -- DONE SEPARATELY -- 8061: Fixes WCM-790: Fallout from CHK-2168 and CHK-2169 8081: Fix for WCM-1018 8082: Merged V2.1 to V2.2 8016: Merged V2.1-A to V2.1 8000: Additional indexes for AVM 8013: Patch to introduce reverse indexes required for AVM git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8474 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/content-services-context.xml | 3 + config/alfresco/core-services-context.xml | 85 +++++--- .../AlfrescoPostCreate-2.2-Extra.sql | 4 + ...lfrescoSchemaUpdate-2.1-NotNullColumns.sql | 21 ++ ...pgrade-4-extra-indexes-and-constraints.sql | 4 + .../messages/content-service.properties | 2 + .../org/alfresco/repo/avm/AVMServiceTest.java | 14 +- .../org/alfresco/repo/avm/AVMStoreImpl.java | 2 +- .../alfresco/repo/avm/AVMSyncServiceImpl.java | 2 +- .../repo/avm/LayeredDirectoryNodeImpl.java | 4 +- .../java/org/alfresco/repo/avm/LookupKey.java | 32 +-- .../repo/avm/PlainDirectoryNodeImpl.java | 2 +- .../alfresco/repo/avm/util/SimplePath.java | 20 +- .../AbstractMappingMetadataExtracter.java | 197 +++++++++++++++++- .../AbstractMetadataExtracterTest.java | 12 +- .../metadata/HtmlMetadataExtracterTest.java | 1 + .../metadata/OfficeMetadataExtracterTest.java | 1 + .../OpenDocumentMetadataExtracterTest.java | 1 + .../OpenOfficeMetadataExtracterTest.java | 3 +- .../metadata/PdfBoxMetadataExtracterTest.java | 1 + .../repo/deploy/DeploymentServiceImpl.java | 79 ++++++- .../service/cmr/repository/NodeService.java | 5 + 22 files changed, 428 insertions(+), 67 deletions(-) create mode 100644 config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.AlfrescoSQLServerDialect/AlfrescoSchemaUpdate-2.1-NotNullColumns.sql diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index 4473429f28..a9d838d7e3 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -145,6 +145,9 @@ + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 527a38e409..e63699b04f 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -14,7 +14,7 @@ - true @@ -24,7 +24,7 @@ classpath:alfresco/repository.properties classpath:alfresco/version.properties classpath:alfresco/domain/transaction.properties - classpath:alfresco/jndi.properties + @@ -38,7 +38,7 @@ - true @@ -55,17 +55,17 @@ - - + lazy-init="true"> @@ -94,7 +94,7 @@ - @@ -113,10 +113,10 @@ - - @@ -189,7 +189,7 @@ false - + @@ -218,7 +218,7 @@ ${server.transaction.allow-writes} - + @@ -236,7 +236,7 @@ false - + PROPAGATION_REQUIRED @@ -354,7 +354,7 @@ - + @@ -417,7 +417,7 @@ 5 - + - + @@ -545,7 +545,7 @@ - + @@ -666,7 +666,6 @@ - @@ -676,7 +675,21 @@ - + + + + + + + + + + + + + + + @@ -731,12 +744,12 @@ alfresco/model/applicationModel.xml alfresco/model/wcmAppModel.xml - + org/alfresco/repo/action/actionModel.xml org/alfresco/repo/rule/ruleModel.xml org/alfresco/repo/version/version_model.xml - + alfresco/model/emailServerModel.xml @@ -771,6 +784,24 @@ + + + + + + + + + + + + + + + + + + @@ -843,7 +874,7 @@ - + @@ -875,13 +906,13 @@ - + - + @@ -904,7 +935,7 @@ - + 2 @@ -951,7 +982,7 @@ - + @@ -981,7 +1012,7 @@ - + @@ -995,7 +1026,7 @@ - + diff --git a/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql b/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql index fa843d4bb8..a188f1d2f4 100644 --- a/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql +++ b/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql @@ -26,3 +26,7 @@ ALTER TABLE avm_aspects_new ADD CONSTRAINT fk_avm_na_qn FOREIGN KEY (qname_id) R CREATE INDEX fk_avm_np_qn ON avm_node_properties_new (qname_id); ALTER TABLE avm_node_properties_new ADD CONSTRAINT fk_avm_np_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); + +CREATE INDEX idx_avm_hl_revpk ON avm_history_links (descendent, ancestor); + +CREATE INDEX idx_avm_vr_revuq ON avm_version_roots (avm_store_id, version_id); diff --git a/config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.AlfrescoSQLServerDialect/AlfrescoSchemaUpdate-2.1-NotNullColumns.sql b/config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.AlfrescoSQLServerDialect/AlfrescoSchemaUpdate-2.1-NotNullColumns.sql new file mode 100644 index 0000000000..0cd5a6ebda --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.AlfrescoSQLServerDialect/AlfrescoSchemaUpdate-2.1-NotNullColumns.sql @@ -0,0 +1,21 @@ +-- +-- Title: Add text columns that allow null +-- Database: SQL Server +-- Since: V2.1 Schema 64 +-- Author: Derek Hulley +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- This is a Sybase issue, so nothing is required here. + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V2.1-NotNullColumns'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V2.1-NotNullColumns', 'Manually executed script upgrade V2.1: Add nullable columns', + 0, 63, -1, 64, null, 'UNKOWN', 1, 1, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/upgrade-4-extra-indexes-and-constraints.sql b/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/upgrade-4-extra-indexes-and-constraints.sql index 333e1fc9d3..143e5b0492 100644 --- a/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/upgrade-4-extra-indexes-and-constraints.sql +++ b/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/upgrade-4-extra-indexes-and-constraints.sql @@ -61,6 +61,10 @@ ALTER TABLE avm_aspects_new ADD CONSTRAINT fk_avm_na_qn FOREIGN KEY (qname_id) R CREATE INDEX fk_avm_np_qn ON avm_node_properties_new (qname_id); ALTER TABLE avm_node_properties_new ADD CONSTRAINT fk_avm_np_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX idx_avm_hl_revpk ON avm_history_links (descendent, ancestor); + +CREATE INDEX idx_avm_vr_revuq ON avm_version_roots (avm_store_id, version_id); + -- -- Record script finish -- diff --git a/config/alfresco/messages/content-service.properties b/config/alfresco/messages/content-service.properties index b52c90dfdb..4641308752 100644 --- a/config/alfresco/messages/content-service.properties +++ b/config/alfresco/messages/content-service.properties @@ -13,3 +13,5 @@ content.http_reader.err.no_connection=Unable to connect to remote Alfresco serve content.http_reader.err.no_authentication=The HTTP reader was unable to authenticate on the remote server: {0} \n content.http_reader.err.check_cluster=Please ensure that 'replicateUpdates' and 'replicateUpdatesViaCopy' is enabled for the cache 'org.alfresco.cache.ticketsCache'. Check that the general cluster configuration is correct and working. content.http_reader.err.unrecognized=An unrecognized error occured when attempting to download content from remote server:\n Server: {0} \n Content: {1} \n HTTP Response: {2} + +metadata.extraction.err.type_conversion=Metadata extraction failed because an extracted value failed to convert to the required type: \n Extractor: {0} \n Target Property QName: {1} \n Required Type: {2} \n Extracted Value: {3} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index 6748ec5ec1..5aa8365b3c 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -68,6 +68,7 @@ import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.avm.LayeringDescriptor; import org.alfresco.service.cmr.avm.VersionDescriptor; +import org.alfresco.service.cmr.avm.deploy.DeploymentEvent; import org.alfresco.service.cmr.avm.deploy.DeploymentReport; import org.alfresco.service.cmr.avm.deploy.DeploymentService; import org.alfresco.service.cmr.avmsync.AVMDifference; @@ -96,7 +97,7 @@ import org.alfresco.util.Pair; /** * Big test of AVM behavior. - * + * * @author britt */ public class AVMServiceTest extends AVMServiceTestBase @@ -612,6 +613,15 @@ public class AVMServiceTest extends AVMServiceTestBase runQueriesForCreateAndDeploy("target"); assertEquals(fService.lookup(-1, "main:/a/b/biz").getGuid(), fService.lookup(-1, "target:/a/b/biz").getGuid()); fService.removeNode("main:/a/b/c/foo"); + fService.createFile("main:/a/b/c", "Foo").close(); + ContentWriter writer = fService.getContentWriter("main:/a/b/c/Foo"); + writer.setEncoding("UTF-8"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.putContent("I am main:/a/b/c/foo"); + report = depService.deployDifference(-1, "main:/a", "localhost", 50500, "admin", "admin", "target:/a", matcher, false, false, false, null); + System.out.println(report); + assertEquals(DeploymentEvent.Type.UPDATED, report.iterator().next().getType()); + fService.removeNode("main:/a/b/c/foo"); report = depService.deployDifference(-1, "main:/a", "localhost", 50500, "admin", "admin", "target:/a", matcher, false, true, false, null); runQueriesForCreateAndDeploy("target"); System.out.println(report); @@ -5531,7 +5541,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test async indexing. - * + * * @throws Exception */ public void testAsyncIndex() throws Exception diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index cdf3cccf44..5879e25d07 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -695,7 +695,7 @@ public class AVMStoreImpl implements AVMStore, Serializable private SortedMap translateListing(Map listing, Lookup lPath) { - SortedMap results = new TreeMap(); + SortedMap results = new TreeMap(String.CASE_INSENSITIVE_ORDER); for (String name : listing.keySet()) { // TODO consider doing this at a lower level. diff --git a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java index 19017486b1..dc043fa1bb 100644 --- a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java @@ -778,7 +778,7 @@ public class AVMSyncServiceImpl implements AVMSyncService return false; } // layer and underlying must match for flattening to be useful. - if (!layer.getIndirection().equals(underlying.getPath())) + if (!layer.getIndirection().equalsIgnoreCase(underlying.getPath())) { return false; } diff --git a/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java b/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java index 3f989c22dd..b6cf33cc0e 100644 --- a/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java @@ -514,7 +514,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec public SortedMap getListingDirect(AVMNodeDescriptor dir, boolean includeDeleted) { List children = AVMDAOs.Instance().fChildEntryDAO.getByParent(this); - SortedMap listing = new TreeMap(); + SortedMap listing = new TreeMap(String.CASE_INSENSITIVE_ORDER); for (ChildEntry child : children) { AVMNode childNode = child.getChild(); @@ -551,7 +551,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec { throw new AVMBadArgumentException("Illegal null argument."); } - SortedMap baseListing = new TreeMap(); + SortedMap baseListing = new TreeMap(String.CASE_INSENSITIVE_ORDER); // If we are not opaque, get the underlying base listing. if (!fOpacity) { diff --git a/source/java/org/alfresco/repo/avm/LookupKey.java b/source/java/org/alfresco/repo/avm/LookupKey.java index 8e425ec192..7cf604d96b 100644 --- a/source/java/org/alfresco/repo/avm/LookupKey.java +++ b/source/java/org/alfresco/repo/avm/LookupKey.java @@ -1,5 +1,5 @@ /** - * + * */ package org.alfresco.repo.avm; @@ -19,22 +19,22 @@ public class LookupKey implements Serializable * The name of the store. */ private String fStoreName; - + /** * The path being looked up. */ private SimplePath fPath; - + /** * The version being looked up. */ private int fVersion; - + /** * Whether the lookup is a write lookup. */ private boolean fWrite; - + /** * Whether the lookup includes deleted nodes. */ @@ -48,7 +48,7 @@ public class LookupKey implements Serializable * @param write Whether this is a write lookup. * @param includeDeleted Whether this lookup should include deleted items. */ - public LookupKey(int version, + public LookupKey(int version, SimplePath path, String storeName, boolean write, @@ -60,7 +60,7 @@ public class LookupKey implements Serializable fWrite = write; fIncludeDeleted = includeDeleted; } - + public LookupKey(LookupKey other) { fVersion = other.fVersion; @@ -69,7 +69,7 @@ public class LookupKey implements Serializable fWrite = other.fWrite; fIncludeDeleted = other.fIncludeDeleted; } - + /** * Set the writeness of this key. */ @@ -86,7 +86,7 @@ public class LookupKey implements Serializable { return fStoreName; } - + /** * Is this a write lookup. * @return Whether this is a write lookup. @@ -95,12 +95,12 @@ public class LookupKey implements Serializable { return fWrite; } - + /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(Object obj) + public boolean equals(Object obj) { if (this == obj) { @@ -111,7 +111,7 @@ public class LookupKey implements Serializable return false; } LookupKey o = (LookupKey)obj; - return fStoreName.equals(o.fStoreName) && + return fStoreName.equalsIgnoreCase(o.fStoreName) && fVersion == o.fVersion && fPath.equals(o.fPath) && fWrite == o.fWrite && @@ -122,9 +122,9 @@ public class LookupKey implements Serializable * @see java.lang.Object#hashCode() */ @Override - public int hashCode() + public int hashCode() { - int hash = fStoreName.hashCode(); + int hash = fStoreName.toLowerCase().hashCode(); hash += fPath.hashCode(); hash += fVersion; hash += fWrite ? 1 : 0; @@ -136,8 +136,8 @@ public class LookupKey implements Serializable * @see java.lang.Object#toString() */ @Override - public String toString() + public String toString() { return fStoreName + ":" + fPath + "-" + fVersion + "-" + fWrite + "-" + fIncludeDeleted; - } + } } diff --git a/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java b/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java index aeef8408e6..f0596f1e5b 100644 --- a/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java @@ -163,7 +163,7 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory { throw new AVMBadArgumentException("Path is null."); } - SortedMap result = new TreeMap(); + SortedMap result = new TreeMap(String.CASE_INSENSITIVE_ORDER); List children = AVMDAOs.Instance().fChildEntryDAO.getByParent(this); for (ChildEntry child : children) { diff --git a/source/java/org/alfresco/repo/avm/util/SimplePath.java b/source/java/org/alfresco/repo/avm/util/SimplePath.java index 21cbeb9c20..814544c437 100644 --- a/source/java/org/alfresco/repo/avm/util/SimplePath.java +++ b/source/java/org/alfresco/repo/avm/util/SimplePath.java @@ -1,5 +1,5 @@ /** - * + * */ package org.alfresco.repo.avm.util; @@ -9,7 +9,7 @@ import java.io.Serializable; * Holds a simple path. * @author britt */ -public class SimplePath implements Serializable +public class SimplePath implements Serializable { private static final long serialVersionUID = 2696828491008988470L; @@ -17,7 +17,7 @@ public class SimplePath implements Serializable * The names of the path's components. */ private String [] fNames; - + /** * Construct a new one from a string. * @param path The String representation of the path. @@ -39,7 +39,7 @@ public class SimplePath implements Serializable } fNames = path.split("/+"); } - + /** * Get the component name at index. * @param index The index of the component to get. @@ -49,7 +49,7 @@ public class SimplePath implements Serializable { return fNames[index]; } - + /** * Get the number of components in this path. * @return The number of components. @@ -63,7 +63,7 @@ public class SimplePath implements Serializable * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(Object obj) + public boolean equals(Object obj) { if (this == obj) { @@ -80,7 +80,7 @@ public class SimplePath implements Serializable } for (int i = 0; i < fNames.length; i++) { - if (!fNames[i].equals(o.fNames[i])) + if (!fNames[i].equalsIgnoreCase(o.fNames[i])) { return false; } @@ -92,12 +92,12 @@ public class SimplePath implements Serializable * @see java.lang.Object#hashCode() */ @Override - public int hashCode() + public int hashCode() { int hash = 0; for (String name : fNames) { - hash += name.hashCode(); + hash += name.toLowerCase().hashCode(); } return hash; } @@ -106,7 +106,7 @@ public class SimplePath implements Serializable * @see java.lang.Object#toString() */ @Override - public String toString() + public String toString() { StringBuilder builder = new StringBuilder(); for (String name : fNames) diff --git a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java index ef637cd426..aafe80a39e 100644 --- a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java @@ -27,21 +27,33 @@ package org.alfresco.repo.content.metadata; import java.io.InputStream; import java.io.Serializable; import java.lang.reflect.Array; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; 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.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.TypeConversionException; import org.alfresco.service.namespace.InvalidQNameException; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -88,15 +100,19 @@ import org.apache.commons.logging.LogFactory; abstract public class AbstractMappingMetadataExtracter implements MetadataExtracter { public static final String NAMESPACE_PROPERTY_PREFIX = "namespace.prefix."; + private static final String ERR_TYPE_CONVERSION = "metadata.extraction.err.type_conversion"; protected static Log logger = LogFactory.getLog(AbstractMappingMetadataExtracter.class); private MetadataExtracterRegistry registry; private MimetypeService mimetypeService; + private DictionaryService dictionaryService; private boolean initialized; private Set supportedMimetypes; private OverwritePolicy overwritePolicy; + private boolean failOnTypeConversion; + private Set supportedDateFormats; private Map> mapping; private boolean inheritDefaultMapping; @@ -124,6 +140,8 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac this.supportedMimetypes = supportedMimetypes; // Set defaults overwritePolicy = OverwritePolicy.PRAGMATIC; + failOnTypeConversion = true; + supportedDateFormats = new HashSet(0); mapping = null; // The default will be fetched inheritDefaultMapping = false; // Any overrides are complete initialized = false; @@ -156,7 +174,15 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac { return mimetypeService; } - + + /** + * @param dictionaryService the dictionary service to determine which data conversions are necessary + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + /** * Set the mimetypes that are supported by the extracter. * @@ -212,6 +238,46 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac this.overwritePolicy = OverwritePolicy.valueOf(overwritePolicyStr); } + /** + * Set whether the extractor should discard metadata that fails to convert to the target type + * defined in the data dictionary model. This is true by default i.e. if the data + * extracted is not compatible with the target model then the extraction will fail. If this is + * false then any extracted data that fails to convert will be discarded. + * + * @param failOnTypeConversion false to discard properties that can't get converted + * to the dictionary-defined type, or true (default) + * to fail the extraction if the type doesn't convert + */ + public void setFailOnTypeConversion(boolean failOnTypeConversion) + { + this.failOnTypeConversion = failOnTypeConversion; + } + + /** + * Set the date formats, over and above the {@link ISO8601DateFormat ISO8601 format}, that will + * be supported for string to date conversions. The supported syntax is described by the + * {@link http://java.sun.com/j2se/1.5.0/docs/api/java/text/SimpleDateFormat.html SimpleDateFormat Javadocs}. + * + * @param supportedDateFormats a list of supported date formats. + */ + public void setSupportedDateFormats(List supportedDateFormats) + { + this.supportedDateFormats = new HashSet(5); + for (String dateFormatStr : supportedDateFormats) + { + try + { + DateFormat df = new SimpleDateFormat(dateFormatStr); + this.supportedDateFormats.add(df); + } + catch (Throwable e) + { + // No good + throw new AlfrescoRuntimeException("Unable to set supported date format: " + dateFormatStr, e); + } + } + } + /** * Set if the property mappings augment or override the mapping generically provided by the * extracter implementation. The default is false, i.e. any mapping set completely @@ -347,6 +413,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac * * @see #setMappingProperties(Properties) */ + @SuppressWarnings("unchecked") protected Map> readMappingProperties(Properties mappingProperties) { Map namespacesByPrefix = new HashMap(5); @@ -562,6 +629,8 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac Map rawMetadata = extractRaw(reader); // Convert to system properties (standalone) Map systemProperties = mapRawToSystem(rawMetadata); + // Convert the properties according to the dictionary types + systemProperties = convertSystemPropertyValues(systemProperties); // Now use the proper overwrite policy changedProperties = overwritePolicy.applyProperties(systemProperties, destination); } @@ -628,6 +697,131 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac return systemProperties; } + /** + * Converts all values according to their dictionary-defined type. This uses the + * {@link #setFailOnTypeConversion(boolean) failOnTypeConversion flag} to determine how failures + * are handled i.e. if values fail to convert, the process may discard the property. + * + * @param systemProperties the values keyed to system property names + * @return Returns a modified map of properties that have been converted. + */ + @SuppressWarnings("unchecked") + private Map convertSystemPropertyValues(Map systemProperties) + { + Map convertedProperties = new HashMap(systemProperties.size() + 7); + for (Map.Entry entry : systemProperties.entrySet()) + { + QName propertyQName = entry.getKey(); + Serializable propertyValue = entry.getValue(); + // Get the property definition + PropertyDefinition propertyDef = (dictionaryService == null) ? null : dictionaryService.getProperty(propertyQName); + if (propertyDef == null) + { + // There is nothing in the DD about this so just transfer it + convertedProperties.put(propertyQName, propertyValue); + continue; + } + // It is in the DD, so attempt the conversion + DataTypeDefinition propertyTypeDef = propertyDef.getDataType(); + Serializable convertedPropertyValue = null; + + try + { + // Attempt to make any date conversions + if (propertyTypeDef.getName().equals(DataTypeDefinition.DATE) || propertyTypeDef.getName().equals(DataTypeDefinition.DATETIME)) + { + if (propertyValue instanceof Collection) + { + convertedPropertyValue = (Serializable) makeDates((Collection) propertyValue); + } + else if (propertyValue instanceof String) + { + convertedPropertyValue = makeDate((String) propertyValue); + } + } + else + { + if (propertyValue instanceof Collection) + { + convertedPropertyValue = (Serializable) DefaultTypeConverter.INSTANCE.convert( + propertyTypeDef, + (Collection) propertyValue); + } + else + { + convertedPropertyValue = (Serializable) DefaultTypeConverter.INSTANCE.convert( + propertyTypeDef, + propertyValue); + } + } + convertedProperties.put(propertyQName, convertedPropertyValue); + } + catch (TypeConversionException e) + { + // Do we just absorb this or is it a problem? + if (failOnTypeConversion) + { + throw AlfrescoRuntimeException.create( + e, + ERR_TYPE_CONVERSION, + this, + propertyQName, + propertyTypeDef.getName(), + propertyValue); + } + } + } + // Done + return convertedProperties; + } + + /** + * Convert a collection of date String to Date objects + */ + private Collection makeDates(Collection dateStrs) + { + List dates = new ArrayList(dateStrs.size()); + for (String dateStr : dateStrs) + { + Date date = makeDate(dateStr); + dates.add(date); + } + return dates; + } + + /** + * Convert a date String to a Date object + */ + private Date makeDate(String dateStr) + { + Date date = null; + try + { + date = DefaultTypeConverter.INSTANCE.convert(Date.class, dateStr); + } + catch (TypeConversionException e) + { + // Try one of the other formats + for (DateFormat df : this.supportedDateFormats) + { + try + { + date = df.parse(dateStr); + } + catch (ParseException ee) + { + // Didn't work + } + } + if (date == null) + { + // Still no luck + throw new TypeConversionException("Unable to convert string to date: " + dateStr); + } + } + return date; + } + /** * Adds a value to the map if it is non-trivial. A value is trivial if *
    @@ -646,6 +840,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac * @param destination the map to put values into * @return Returns true if set, otherwise false */ + @SuppressWarnings("unchecked") protected boolean putRawValue(String key, Serializable value, Map destination) { if (value == null) diff --git a/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java b/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java index 4bf7a04d73..41b4bbc7f1 100644 --- a/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java +++ b/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java @@ -36,7 +36,9 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.filestore.FileContentReader; import org.alfresco.repo.content.transform.AbstractContentTransformerTest; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.namespace.QName; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.TempFileProvider; @@ -57,6 +59,7 @@ public abstract class AbstractMetadataExtracterTest extends TestCase protected static final String QUICK_CREATOR = "Nevin Nollop"; protected MimetypeMap mimetypeMap; + protected DictionaryService dictionaryService; protected abstract MetadataExtracter getExtracter(); @@ -67,12 +70,13 @@ public abstract class AbstractMetadataExtracterTest extends TestCase public void setUp() throws Exception { this.mimetypeMap = (MimetypeMap) ctx.getBean("mimetypeService"); + this.dictionaryService = (DictionaryService) ctx.getBean("dictionaryService"); // perform a little cleaning up long now = System.currentTimeMillis(); TempFileProvider.TempFileCleanerJob.removeFiles(now); } - + /** * Check that all objects are present */ @@ -123,9 +127,11 @@ public abstract class AbstractMetadataExtracterTest extends TestCase { assertEquals( "Property " + ContentModel.PROP_TITLE + " not found for mimetype " + mimetype, - QUICK_TITLE, properties.get(ContentModel.PROP_TITLE)); + QUICK_TITLE, + DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_TITLE))); assertEquals( "Property " + ContentModel.PROP_DESCRIPTION + " not found for mimetype " + mimetype, - QUICK_DESCRIPTION, properties.get(ContentModel.PROP_DESCRIPTION)); + QUICK_DESCRIPTION, + DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_DESCRIPTION))); } } diff --git a/source/java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java b/source/java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java index 756e293621..4f043c8607 100644 --- a/source/java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java +++ b/source/java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java @@ -38,6 +38,7 @@ public class HtmlMetadataExtracterTest extends AbstractMetadataExtracterTest { super.setUp(); extracter = new HtmlMetadataExtracter(); + extracter.setDictionaryService(dictionaryService); extracter.register(); } diff --git a/source/java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java b/source/java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java index 70bd2dd344..2108e45e2f 100644 --- a/source/java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java +++ b/source/java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java @@ -15,6 +15,7 @@ public class OfficeMetadataExtracterTest extends AbstractMetadataExtracterTest { super.setUp(); extracter = new OfficeMetadataExtracter(); + extracter.setDictionaryService(dictionaryService); extracter.register(); } diff --git a/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java b/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java index 5aa9fc4a29..a2760b0845 100644 --- a/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java +++ b/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java @@ -15,6 +15,7 @@ public class OpenDocumentMetadataExtracterTest extends AbstractMetadataExtracter { super.setUp(); extracter = new OpenDocumentMetadataExtracter(); + extracter.setDictionaryService(dictionaryService); extracter.register(); } diff --git a/source/java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java b/source/java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java index c336a89b94..315bd6d749 100644 --- a/source/java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java +++ b/source/java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java @@ -23,8 +23,6 @@ package org.alfresco.repo.content.metadata; import net.sf.jooreports.openoffice.connection.OpenOfficeConnection; -import net.sf.jooreports.openoffice.connection.SocketOpenOfficeConnection; - /** * @author Jesper Steen Møller @@ -42,6 +40,7 @@ public class OpenOfficeMetadataExtracterTest extends AbstractMetadataExtracterTe extracter = new OpenOfficeMetadataExtracter(); extracter.setMimetypeService(mimetypeMap); + extracter.setDictionaryService(dictionaryService); extracter.setConnection(connection); extracter.init(); } diff --git a/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java b/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java index 295c283e92..0d1329c63c 100644 --- a/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java +++ b/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java @@ -16,6 +16,7 @@ public class PdfBoxMetadataExtracterTest extends AbstractMetadataExtracterTest { super.setUp(); extracter = new PdfBoxMetadataExtracter(); + extracter.setDictionaryService(dictionaryService); extracter.register(); } diff --git a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java index 6ccd67dc5b..8986e1ad3e 100644 --- a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java +++ b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java @@ -70,6 +70,8 @@ import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.namespace.QName; import org.alfresco.util.NameMatcher; import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.remoting.rmi.RmiProxyFactoryBean; /** @@ -78,6 +80,8 @@ import org.springframework.remoting.rmi.RmiProxyFactoryBean; */ public class DeploymentServiceImpl implements DeploymentService { + private static Log fgLogger = LogFactory.getLog(DeploymentServiceImpl.class); + /** * Class to hold Deployment destination information. * Used as a lock to serialize deployments to the same @@ -122,6 +126,11 @@ public class DeploymentServiceImpl implements DeploymentService { return fHost.hashCode() + fPort; } + + public String toString() + { + return fHost; + } }; /** @@ -169,6 +178,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentDestination dest = getLock(hostName, port); synchronized (dest) { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Deploying to Remote Alfresco at " + dest); + } try { DeploymentReport report = new DeploymentReport(); @@ -178,6 +191,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.START, new Pair(version, srcPath), dstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } for (DeploymentCallback callback : callbacks) { callback.eventOccurred(event); @@ -229,6 +246,10 @@ public class DeploymentServiceImpl implements DeploymentService new DeploymentEvent(DeploymentEvent.Type.COPIED, new Pair(version, srcPath), dstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } report.add(event); if (callbacks != null) { @@ -248,6 +269,10 @@ public class DeploymentServiceImpl implements DeploymentService event = new DeploymentEvent(DeploymentEvent.Type.END, new Pair(version, srcPath), dstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } for (DeploymentCallback callback : callbacks) { callback.eventOccurred(event); @@ -269,6 +294,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.END, new Pair(version, srcPath), dstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } for (DeploymentCallback callback : callbacks) { callback.eventOccurred(event); @@ -381,6 +410,10 @@ public class DeploymentServiceImpl implements DeploymentService new DeploymentEvent(DeploymentEvent.Type.DELETED, source, destination); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } report.add(event); if (callbacks != null) { @@ -428,6 +461,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, source, destination); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } report.add(event); if (callbacks != null) { @@ -449,6 +486,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, source, destination); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } report.add(event); if (callbacks != null) { @@ -482,6 +523,10 @@ public class DeploymentServiceImpl implements DeploymentService String destination = dst.getPath(); DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, source, destination); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } report.add(event); if (callbacks != null) { @@ -512,6 +557,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.UPDATED, source, destination); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } report.add(event); if (callbacks != null) { @@ -536,6 +585,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.UPDATED, source, destination); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } report.add(event); if (callbacks != null) { @@ -798,6 +851,10 @@ public class DeploymentServiceImpl implements DeploymentService boolean dontDelete, boolean dontDo, List callbacks) { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Deploying To FileSystem Reciever on " + hostName + " to target " + target); + } DeploymentReport report = new DeploymentReport(); DeploymentReceiverService service = null; String ticket = null; @@ -807,6 +864,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.START, new Pair(version, srcPath), target); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } if (callbacks != null) { for (DeploymentCallback callback : callbacks) @@ -893,6 +954,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.DELETED, new Pair(version, extendPath(srcPath, dst.getName())), newDstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } if (callbacks != null) { for (DeploymentCallback callback : callbacks) @@ -915,7 +980,7 @@ public class DeploymentServiceImpl implements DeploymentService src = null; continue; } - int diff = src.getName().compareTo(dst.getName()); + int diff = src.getName().compareToIgnoreCase(dst.getName()); if (diff < 0) { if (!excluded(matcher, src.getPath(), null)) @@ -973,6 +1038,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.DELETED, new Pair(version, extendPath(srcPath, dst.getName())), newDstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } if (callbacks != null) { for (DeploymentCallback callback : callbacks) @@ -1008,6 +1077,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, new Pair(version, src.getPath()), dstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } if (callbacks != null) { for (DeploymentCallback callback : callbacks) @@ -1049,6 +1122,10 @@ public class DeploymentServiceImpl implements DeploymentService DeploymentEvent event = new DeploymentEvent(DeploymentEvent.Type.COPIED, new Pair(version, src.getPath()), dstPath); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(event); + } if (callbacks != null) { for (DeploymentCallback callback : callbacks) diff --git a/source/java/org/alfresco/service/cmr/repository/NodeService.java b/source/java/org/alfresco/service/cmr/repository/NodeService.java index fa59c93f48..76e38fd767 100644 --- a/source/java/org/alfresco/service/cmr/repository/NodeService.java +++ b/source/java/org/alfresco/service/cmr/repository/NodeService.java @@ -480,6 +480,11 @@ public interface NodeService /** * Get the node with the given name within the context of the parent node. The name * is case-insensitive as Alfresco has to support case-insensitive clients as standard. + *

    + * That API method getChildByName only works for associations that don't allow duplicate child names. + * See cm:folder and the duplicate tag. Child associations without this allow duplicate + * child names and therefore it is possible to have multiple children with the same name stored against + * the given association type. * * @param nodeRef the parent node - usuall a container * @param assocTypeQName the type of the association