From 264b8f4eedc0283170c128950c7c01bda4abc9d5 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 30 Aug 2016 17:32:57 +0100 Subject: [PATCH 001/282] Initialize git repo for 'alfresco-core' --- .gitattributes | 8 ++++++++ .gitignore | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..8303247a39 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +.* eol=crlf +*.html eol=crlf +*.java eol=crlf +*.txt eol=crlf +*.css eol=crlf +*.xml eol=crlf +*.js eol=crlf +*.properties eol=crlf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..9346843361 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +*.class + +# Eclipse +.classpath +.settings +.project + +# Intellij +.idea/ +*.iml +*.iws + +# Mac +.DS_Store + +# Maven +target +*.log +*.log.* + +# Mobile Tools for Java (J2ME) + +.mtj +.tmp/ + +# Package Files # + +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml + +hs_err_pid* From 3cd73ed8dc98f7d2f29508e935e137a97d02609e Mon Sep 17 00:00:00 2001 From: dhulley Date: Wed, 31 Aug 2016 18:16:27 +0100 Subject: [PATCH 002/282] Transfer code from svn to gitlab.alfresco.com --- pom.xml | 233 ++++ .../org/alfresco/api/AlfrescoPublicApi.java | 41 + .../config/AlfrescoPropertiesPersister.java | 77 ++ .../config/JndiObjectFactoryBean.java | 68 + .../config/JndiPropertiesFactoryBean.java | 60 + .../JndiPropertyPlaceholderConfigurer.java | 57 + .../NonBlockingLazyInitTargetSource.java | 67 + .../alfresco/config/PathMatchingHelper.java | 69 ++ .../config/SystemPropertiesFactoryBean.java | 141 +++ .../config/SystemPropertiesSetterBean.java | 94 ++ .../encoding/AbstractCharactersetFinder.java | 172 +++ .../encoding/BomCharactersetFinder.java | 115 ++ .../alfresco/encoding/CharactersetFinder.java | 68 + .../encoding/GuessEncodingCharsetFinder.java | 84 ++ .../encryption/AbstractEncryptor.java | 312 +++++ .../encryption/AbstractKeyProvider.java | 35 + .../alfresco/encryption/AlfrescoKeyStore.java | 132 ++ .../encryption/AlfrescoKeyStoreImpl.java | 1100 +++++++++++++++++ .../org/alfresco/encryption/CachedKey.java | 59 + .../encryption/DecryptingInputStream.java | 355 ++++++ .../encryption/DefaultEncryptionUtils.java | 480 +++++++ .../alfresco/encryption/DefaultEncryptor.java | 268 ++++ .../encryption/DefaultFallbackEncryptor.java | 230 ++++ .../encryption/EncryptingOutputStream.java | 252 ++++ .../encryption/EncryptionKeysRegistry.java | 83 ++ .../alfresco/encryption/EncryptionUtils.java | 104 ++ .../org/alfresco/encryption/Encryptor.java | 118 ++ .../encryption/FallbackEncryptor.java | 38 + .../encryption/GenerateSecretKey.java | 47 + .../encryption/InvalidKeystoreException.java | 34 + .../java/org/alfresco/encryption/KeyMap.java | 74 ++ .../org/alfresco/encryption/KeyProvider.java | 48 + .../encryption/KeyResourceLoader.java | 53 + .../encryption/KeyStoreParameters.java | 121 ++ .../org/alfresco/encryption/KeysReport.java | 50 + .../encryption/KeystoreKeyProvider.java | 95 ++ .../org/alfresco/encryption/MACUtils.java | 291 +++++ .../encryption/MissingKeyException.java | 53 + .../encryption/SpringKeyResourceLoader.java | 157 +++ .../ssl/AuthSSLInitializationError.java | 53 + .../ssl/AuthSSLProtocolSocketFactory.java | 210 ++++ .../ssl/SSLEncryptionParameters.java | 67 + .../error/AlfrescoRuntimeException.java | 231 ++++ .../alfresco/error/ExceptionStackUtil.java | 57 + .../org/alfresco/error/StackTraceUtil.java | 68 + .../alfresco/hibernate/DialectFactory.java | 177 +++ .../hibernate/DialectFactoryBean.java | 136 ++ .../httpclient/AbstractHttpClient.java | 209 ++++ .../httpclient/AlfrescoHttpClient.java | 30 + .../httpclient/AuthenticationException.java | 21 + .../org/alfresco/httpclient/GetRequest.java | 32 + .../org/alfresco/httpclient/HeadRequest.java | 32 + .../httpclient/HttpClientFactory.java | 777 ++++++++++++ .../httpclient/HttpMethodResponse.java | 67 + .../httpclient/MD5EncryptionParameters.java | 74 ++ .../org/alfresco/httpclient/PostRequest.java | 44 + .../java/org/alfresco/httpclient/Request.java | 136 ++ .../org/alfresco/httpclient/Response.java | 42 + .../alfresco/httpclient/SecureHttpClient.java | 149 +++ .../ResourceBundleBootstrapComponent.java | 47 + .../java/org/alfresco/ibatis/BatchingDAO.java | 42 + .../alfresco/ibatis/ByteArrayTypeHandler.java | 149 +++ .../HierarchicalSqlSessionFactoryBean.java | 552 +++++++++ .../ibatis/HierarchicalXMLConfigBuilder.java | 395 ++++++ .../java/org/alfresco/ibatis/IdsEntity.java | 76 ++ .../ibatis/RetryingCallbackHelper.java | 154 +++ .../alfresco/ibatis/RollupResultHandler.java | 253 ++++ .../ibatis/SerializableTypeHandler.java | 185 +++ .../org/alfresco/processor/Processor.java | 48 + .../processor/ProcessorExtension.java | 34 + .../AbstractCachingCannedQueryFactory.java | 78 ++ .../alfresco/query/AbstractCannedQuery.java | 347 ++++++ .../query/AbstractCannedQueryFactory.java | 90 ++ .../java/org/alfresco/query/CannedQuery.java | 53 + .../alfresco/query/CannedQueryException.java | 68 + .../alfresco/query/CannedQueryFactory.java | 53 + .../query/CannedQueryPageDetails.java | 188 +++ .../alfresco/query/CannedQueryParameters.java | 222 ++++ .../alfresco/query/CannedQueryResults.java | 68 + .../query/CannedQuerySortDetails.java | 89 ++ .../query/EmptyCannedQueryResults.java | 66 + .../alfresco/query/EmptyPagingResults.java | 66 + .../query/ListBackedPagingResults.java | 91 ++ .../java/org/alfresco/query/PageDetails.java | 93 ++ .../org/alfresco/query/PagingRequest.java | 161 +++ .../org/alfresco/query/PagingResults.java | 79 ++ .../alfresco/query/PermissionedResults.java | 38 + .../org/alfresco/scripts/ScriptException.java | 65 + .../scripts/ScriptResourceHelper.java | 193 +++ .../scripts/ScriptResourceLoader.java | 27 + .../alfresco/util/AbstractTriggerBean.java | 208 ++++ .../org/alfresco/util/ArgumentHelper.java | 114 ++ .../java/org/alfresco/util/BridgeTable.java | 445 +++++++ .../org/alfresco/util/CachingDateFormat.java | 441 +++++++ src/main/java/org/alfresco/util/Content.java | 75 ++ src/main/java/org/alfresco/util/Convert.java | 809 ++++++++++++ .../org/alfresco/util/CronTriggerBean.java | 134 ++ src/main/java/org/alfresco/util/Debug.java | 122 ++ src/main/java/org/alfresco/util/Deleter.java | 123 ++ .../DynamicallySizedThreadPoolExecutor.java | 156 +++ .../java/org/alfresco/util/EqualsHelper.java | 261 ++++ .../org/alfresco/util/ExpiringValueCache.java | 93 ++ .../org/alfresco/util/FileFilterMode.java | 115 ++ src/main/java/org/alfresco/util/GUID.java | 174 +++ src/main/java/org/alfresco/util/IPUtils.java | 26 + .../org/alfresco/util/ISO8601DateFormat.java | 346 ++++++ .../org/alfresco/util/InputStreamContent.java | 120 ++ src/main/java/org/alfresco/util/JMXUtils.java | 63 + .../java/org/alfresco/util/LockHelper.java | 72 ++ .../java/org/alfresco/util/LogAdapter.java | 171 +++ src/main/java/org/alfresco/util/LogTee.java | 117 ++ src/main/java/org/alfresco/util/LogUtil.java | 102 ++ src/main/java/org/alfresco/util/MD5.java | 116 ++ .../java/org/alfresco/util/MaxSizeMap.java | 53 + .../org/alfresco/util/OneToManyBiMap.java | 49 + .../org/alfresco/util/OneToManyHashBiMap.java | 168 +++ .../org/alfresco/util/OneToManyHashMap.java | 190 +++ .../java/org/alfresco/util/OneToManyMap.java | 96 ++ .../java/org/alfresco/util/PackageMarker.java | 22 + src/main/java/org/alfresco/util/Pair.java | 142 +++ .../org/alfresco/util/ParameterCheck.java | 76 ++ .../java/org/alfresco/util/PathMapper.java | 327 +++++ .../java/org/alfresco/util/PatternFilter.java | 69 ++ .../java/org/alfresco/util/PropertyCheck.java | 105 ++ .../alfresco/util/ReadWriteLockExecuter.java | 118 ++ .../org/alfresco/util/ReflectionHelper.java | 192 +++ .../alfresco/util/SchedulerStarterBean.java | 66 + .../org/alfresco/util/SerializationUtils.java | 276 +++++ .../org/alfresco/util/TempFileProvider.java | 506 ++++++++ .../alfresco/util/TraceableThreadFactory.java | 109 ++ .../java/org/alfresco/util/TriggerBean.java | 103 ++ .../org/alfresco/util/TriggerBeanSPI.java | 68 + src/main/java/org/alfresco/util/Triple.java | 131 ++ .../java/org/alfresco/util/VersionNumber.java | 205 +++ .../org/alfresco/util/VmShutdownListener.java | 83 ++ .../org/alfresco/util/bean/BooleanBean.java | 30 + .../util/bean/HierarchicalBeanLoader.java | 251 ++++ .../AbstractAsynchronouslyRefreshedCache.java | 732 +++++++++++ .../cache/AbstractRefreshableCacheEvent.java | 86 ++ .../cache/AsynchronouslyRefreshedCache.java | 20 + .../AsynchronouslyRefreshedCacheRegistry.java | 19 + ...tAsynchronouslyRefreshedCacheRegistry.java | 74 ++ .../alfresco/util/cache/RefreshableCache.java | 29 + .../util/cache/RefreshableCacheEvent.java | 18 + .../util/cache/RefreshableCacheListener.java | 20 + .../cache/RefreshableCacheRefreshEvent.java | 42 + .../cache/RefreshableCacheRefreshedEvent.java | 44 + .../util/collections/CollectionUtils.java | 647 ++++++++++ .../util/collections/EntryTransformer.java | 34 + .../org/alfresco/util/collections/Filter.java | 30 + .../alfresco/util/collections/Function.java | 38 + .../alfresco/util/collections/JsonUtils.java | 59 + .../util/exec/ExecParameterTokenizer.java | 264 ++++ .../org/alfresco/util/exec/RuntimeExec.java | 1005 +++++++++++++++ .../util/exec/RuntimeExecBootstrapBean.java | 257 ++++ .../util/exec/RuntimeExecShutdownBean.java | 165 +++ src/main/java/org/alfresco/util/log/NDC.java | 77 ++ .../org/alfresco/util/log/NDCDelegate.java | 45 + .../org/alfresco/util/log/log4j/Log4JNDC.java | 60 + .../util/random/NormalDistributionHelper.java | 66 + .../util/registry/NamedObjectRegistry.java | 216 ++++ .../resource/HierarchicalResourceLoader.java | 203 +++ .../util/shard/ExplicitShardingPolicy.java | 104 ++ .../transaction/ConnectionPoolException.java | 52 + .../SpringAwareUserTransaction.java | 618 +++++++++ .../util/transaction/TransactionListener.java | 70 ++ .../TransactionListenerAdapter.java | 60 + .../transaction/TransactionSupportUtil.java | 639 ++++++++++ .../org/alfresco/util/xml/SchemaHelper.java | 114 ++ .../servlet/StaticAssetCacheFilter.java | 79 ++ .../servlet/X509ServletFilterBase.java | 326 +++++ src/main/resources/log4j.properties | 264 ++++ src/main/resources/logging.properties | 8 + .../SystemPropertiesSetterBeanTest.java | 88 ++ .../EncryptingOutputStreamTest.java | 69 ++ .../error/AlfrescoRuntimeExceptionTest.java | 116 ++ ...HierarchicalSqlSessionFactoryBeanTest.java | 227 ++++ .../org/alfresco/query/CannedQueryTest.java | 366 ++++++ src/test/java/org/alfresco/util/BaseTest.java | 152 +++ .../org/alfresco/util/BridgeTableTest.java | 362 ++++++ ...ynamicallySizedThreadPoolExecutorTest.java | 334 +++++ .../org/alfresco/util/EqualsHelperTest.java | 107 ++ src/test/java/org/alfresco/util/GuidTest.java | 104 ++ .../alfresco/util/ISO8601DateFormatTest.java | 287 +++++ .../org/alfresco/util/LogAdapterTest.java | 212 ++++ .../java/org/alfresco/util/LogTeeTest.java | 218 ++++ .../org/alfresco/util/PathMapperTest.java | 95 ++ .../alfresco/util/TempFileProviderTest.java | 95 ++ .../org/alfresco/util/VersionNumberTest.java | 123 ++ .../util/bean/HierarchicalBeanLoaderTest.java | 95 ++ .../util/collections/CollectionUtilsTest.java | 206 +++ .../util/exec/ExecParameterTokenizerTest.java | 161 +++ .../util/exec/RuntimeExecBeansTest.java | 227 ++++ .../alfresco/util/exec/RuntimeExecTest.java | 166 +++ .../random/NormalDistributionHelperTest.java | 69 ++ .../HierarchicalResourceLoaderTest.java | 170 +++ .../shard/ExplicitShardingPolicyTest.java | 201 +++ .../SpringAwareUserTransactionTest.java | 404 ++++++ .../hierarchical-bean-loader-test-context.xml | 44 + src/test/resources/config-areas.xml | 13 + src/test/resources/config-multi.xml | 30 + src/test/resources/config-props.properties | 5 + src/test/resources/config-props.xml | 19 + src/test/resources/config-replace.xml | 19 + src/test/resources/config.xml | 57 + .../hierarchy-test-SqlMapConfig.xml | 15 + .../hierarchy-test/hierarchy-test-context.xml | 51 + .../hierarchy-test-SqlMap.xml | 26 + .../hierarchy-test-SqlMap.xml | 26 + .../hierarchy-test-SqlMap.xml | 26 + .../org/alfresco/i18n/testMessages.properties | 4 + .../i18n/testMessages_fr_FR.properties | 4 + .../exec/RuntimeExecBeansTest-context.xml | 225 ++++ .../java.util.AbstractCollection/file.txt | 1 + .../java.util.AbstractList/file.txt | 1 + .../java.util.TreeSet/file.txt | 1 + .../test-config-forms-basic-override.xml | 99 ++ .../resources/test-config-forms-basic.xml | 172 +++ .../resources/test-config-forms-negative.xml | 36 + src/test/resources/test-config-forms.xml | 140 +++ 220 files changed, 32707 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/org/alfresco/api/AlfrescoPublicApi.java create mode 100644 src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java create mode 100644 src/main/java/org/alfresco/config/JndiObjectFactoryBean.java create mode 100644 src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java create mode 100644 src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java create mode 100644 src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java create mode 100644 src/main/java/org/alfresco/config/PathMatchingHelper.java create mode 100644 src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java create mode 100644 src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java create mode 100644 src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java create mode 100644 src/main/java/org/alfresco/encoding/BomCharactersetFinder.java create mode 100644 src/main/java/org/alfresco/encoding/CharactersetFinder.java create mode 100644 src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java create mode 100644 src/main/java/org/alfresco/encryption/AbstractEncryptor.java create mode 100644 src/main/java/org/alfresco/encryption/AbstractKeyProvider.java create mode 100644 src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java create mode 100644 src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java create mode 100644 src/main/java/org/alfresco/encryption/CachedKey.java create mode 100644 src/main/java/org/alfresco/encryption/DecryptingInputStream.java create mode 100644 src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java create mode 100644 src/main/java/org/alfresco/encryption/DefaultEncryptor.java create mode 100644 src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java create mode 100644 src/main/java/org/alfresco/encryption/EncryptingOutputStream.java create mode 100644 src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java create mode 100644 src/main/java/org/alfresco/encryption/EncryptionUtils.java create mode 100644 src/main/java/org/alfresco/encryption/Encryptor.java create mode 100644 src/main/java/org/alfresco/encryption/FallbackEncryptor.java create mode 100644 src/main/java/org/alfresco/encryption/GenerateSecretKey.java create mode 100644 src/main/java/org/alfresco/encryption/InvalidKeystoreException.java create mode 100644 src/main/java/org/alfresco/encryption/KeyMap.java create mode 100644 src/main/java/org/alfresco/encryption/KeyProvider.java create mode 100644 src/main/java/org/alfresco/encryption/KeyResourceLoader.java create mode 100644 src/main/java/org/alfresco/encryption/KeyStoreParameters.java create mode 100644 src/main/java/org/alfresco/encryption/KeysReport.java create mode 100644 src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java create mode 100644 src/main/java/org/alfresco/encryption/MACUtils.java create mode 100644 src/main/java/org/alfresco/encryption/MissingKeyException.java create mode 100644 src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java create mode 100644 src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java create mode 100644 src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java create mode 100644 src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java create mode 100644 src/main/java/org/alfresco/error/AlfrescoRuntimeException.java create mode 100644 src/main/java/org/alfresco/error/ExceptionStackUtil.java create mode 100644 src/main/java/org/alfresco/error/StackTraceUtil.java create mode 100644 src/main/java/org/alfresco/hibernate/DialectFactory.java create mode 100644 src/main/java/org/alfresco/hibernate/DialectFactoryBean.java create mode 100644 src/main/java/org/alfresco/httpclient/AbstractHttpClient.java create mode 100644 src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java create mode 100644 src/main/java/org/alfresco/httpclient/AuthenticationException.java create mode 100644 src/main/java/org/alfresco/httpclient/GetRequest.java create mode 100644 src/main/java/org/alfresco/httpclient/HeadRequest.java create mode 100644 src/main/java/org/alfresco/httpclient/HttpClientFactory.java create mode 100644 src/main/java/org/alfresco/httpclient/HttpMethodResponse.java create mode 100644 src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java create mode 100644 src/main/java/org/alfresco/httpclient/PostRequest.java create mode 100644 src/main/java/org/alfresco/httpclient/Request.java create mode 100644 src/main/java/org/alfresco/httpclient/Response.java create mode 100644 src/main/java/org/alfresco/httpclient/SecureHttpClient.java create mode 100644 src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java create mode 100644 src/main/java/org/alfresco/ibatis/BatchingDAO.java create mode 100644 src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java create mode 100644 src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java create mode 100644 src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java create mode 100644 src/main/java/org/alfresco/ibatis/IdsEntity.java create mode 100644 src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java create mode 100644 src/main/java/org/alfresco/ibatis/RollupResultHandler.java create mode 100644 src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java create mode 100644 src/main/java/org/alfresco/processor/Processor.java create mode 100644 src/main/java/org/alfresco/processor/ProcessorExtension.java create mode 100644 src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java create mode 100644 src/main/java/org/alfresco/query/AbstractCannedQuery.java create mode 100644 src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java create mode 100644 src/main/java/org/alfresco/query/CannedQuery.java create mode 100644 src/main/java/org/alfresco/query/CannedQueryException.java create mode 100644 src/main/java/org/alfresco/query/CannedQueryFactory.java create mode 100644 src/main/java/org/alfresco/query/CannedQueryPageDetails.java create mode 100644 src/main/java/org/alfresco/query/CannedQueryParameters.java create mode 100644 src/main/java/org/alfresco/query/CannedQueryResults.java create mode 100644 src/main/java/org/alfresco/query/CannedQuerySortDetails.java create mode 100644 src/main/java/org/alfresco/query/EmptyCannedQueryResults.java create mode 100644 src/main/java/org/alfresco/query/EmptyPagingResults.java create mode 100644 src/main/java/org/alfresco/query/ListBackedPagingResults.java create mode 100644 src/main/java/org/alfresco/query/PageDetails.java create mode 100644 src/main/java/org/alfresco/query/PagingRequest.java create mode 100644 src/main/java/org/alfresco/query/PagingResults.java create mode 100644 src/main/java/org/alfresco/query/PermissionedResults.java create mode 100644 src/main/java/org/alfresco/scripts/ScriptException.java create mode 100644 src/main/java/org/alfresco/scripts/ScriptResourceHelper.java create mode 100644 src/main/java/org/alfresco/scripts/ScriptResourceLoader.java create mode 100644 src/main/java/org/alfresco/util/AbstractTriggerBean.java create mode 100644 src/main/java/org/alfresco/util/ArgumentHelper.java create mode 100644 src/main/java/org/alfresco/util/BridgeTable.java create mode 100644 src/main/java/org/alfresco/util/CachingDateFormat.java create mode 100644 src/main/java/org/alfresco/util/Content.java create mode 100644 src/main/java/org/alfresco/util/Convert.java create mode 100644 src/main/java/org/alfresco/util/CronTriggerBean.java create mode 100644 src/main/java/org/alfresco/util/Debug.java create mode 100644 src/main/java/org/alfresco/util/Deleter.java create mode 100644 src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java create mode 100644 src/main/java/org/alfresco/util/EqualsHelper.java create mode 100644 src/main/java/org/alfresco/util/ExpiringValueCache.java create mode 100644 src/main/java/org/alfresco/util/FileFilterMode.java create mode 100644 src/main/java/org/alfresco/util/GUID.java create mode 100644 src/main/java/org/alfresco/util/IPUtils.java create mode 100644 src/main/java/org/alfresco/util/ISO8601DateFormat.java create mode 100644 src/main/java/org/alfresco/util/InputStreamContent.java create mode 100644 src/main/java/org/alfresco/util/JMXUtils.java create mode 100644 src/main/java/org/alfresco/util/LockHelper.java create mode 100644 src/main/java/org/alfresco/util/LogAdapter.java create mode 100644 src/main/java/org/alfresco/util/LogTee.java create mode 100644 src/main/java/org/alfresco/util/LogUtil.java create mode 100644 src/main/java/org/alfresco/util/MD5.java create mode 100644 src/main/java/org/alfresco/util/MaxSizeMap.java create mode 100644 src/main/java/org/alfresco/util/OneToManyBiMap.java create mode 100644 src/main/java/org/alfresco/util/OneToManyHashBiMap.java create mode 100644 src/main/java/org/alfresco/util/OneToManyHashMap.java create mode 100644 src/main/java/org/alfresco/util/OneToManyMap.java create mode 100644 src/main/java/org/alfresco/util/PackageMarker.java create mode 100644 src/main/java/org/alfresco/util/Pair.java create mode 100644 src/main/java/org/alfresco/util/ParameterCheck.java create mode 100644 src/main/java/org/alfresco/util/PathMapper.java create mode 100644 src/main/java/org/alfresco/util/PatternFilter.java create mode 100644 src/main/java/org/alfresco/util/PropertyCheck.java create mode 100644 src/main/java/org/alfresco/util/ReadWriteLockExecuter.java create mode 100644 src/main/java/org/alfresco/util/ReflectionHelper.java create mode 100644 src/main/java/org/alfresco/util/SchedulerStarterBean.java create mode 100644 src/main/java/org/alfresco/util/SerializationUtils.java create mode 100644 src/main/java/org/alfresco/util/TempFileProvider.java create mode 100644 src/main/java/org/alfresco/util/TraceableThreadFactory.java create mode 100644 src/main/java/org/alfresco/util/TriggerBean.java create mode 100644 src/main/java/org/alfresco/util/TriggerBeanSPI.java create mode 100644 src/main/java/org/alfresco/util/Triple.java create mode 100644 src/main/java/org/alfresco/util/VersionNumber.java create mode 100644 src/main/java/org/alfresco/util/VmShutdownListener.java create mode 100644 src/main/java/org/alfresco/util/bean/BooleanBean.java create mode 100644 src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java create mode 100644 src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java create mode 100644 src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java create mode 100644 src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java create mode 100644 src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java create mode 100644 src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java create mode 100644 src/main/java/org/alfresco/util/cache/RefreshableCache.java create mode 100644 src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java create mode 100644 src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java create mode 100644 src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java create mode 100644 src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java create mode 100644 src/main/java/org/alfresco/util/collections/CollectionUtils.java create mode 100644 src/main/java/org/alfresco/util/collections/EntryTransformer.java create mode 100644 src/main/java/org/alfresco/util/collections/Filter.java create mode 100644 src/main/java/org/alfresco/util/collections/Function.java create mode 100644 src/main/java/org/alfresco/util/collections/JsonUtils.java create mode 100644 src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java create mode 100644 src/main/java/org/alfresco/util/exec/RuntimeExec.java create mode 100644 src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java create mode 100644 src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java create mode 100644 src/main/java/org/alfresco/util/log/NDC.java create mode 100644 src/main/java/org/alfresco/util/log/NDCDelegate.java create mode 100644 src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java create mode 100644 src/main/java/org/alfresco/util/random/NormalDistributionHelper.java create mode 100644 src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java create mode 100644 src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java create mode 100644 src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java create mode 100644 src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java create mode 100644 src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java create mode 100644 src/main/java/org/alfresco/util/transaction/TransactionListener.java create mode 100644 src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java create mode 100644 src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java create mode 100644 src/main/java/org/alfresco/util/xml/SchemaHelper.java create mode 100644 src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java create mode 100644 src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java create mode 100644 src/main/resources/log4j.properties create mode 100644 src/main/resources/logging.properties create mode 100644 src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java create mode 100644 src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java create mode 100644 src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java create mode 100644 src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java create mode 100644 src/test/java/org/alfresco/query/CannedQueryTest.java create mode 100644 src/test/java/org/alfresco/util/BaseTest.java create mode 100644 src/test/java/org/alfresco/util/BridgeTableTest.java create mode 100644 src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java create mode 100644 src/test/java/org/alfresco/util/EqualsHelperTest.java create mode 100644 src/test/java/org/alfresco/util/GuidTest.java create mode 100644 src/test/java/org/alfresco/util/ISO8601DateFormatTest.java create mode 100644 src/test/java/org/alfresco/util/LogAdapterTest.java create mode 100644 src/test/java/org/alfresco/util/LogTeeTest.java create mode 100644 src/test/java/org/alfresco/util/PathMapperTest.java create mode 100644 src/test/java/org/alfresco/util/TempFileProviderTest.java create mode 100644 src/test/java/org/alfresco/util/VersionNumberTest.java create mode 100644 src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java create mode 100644 src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java create mode 100644 src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java create mode 100644 src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java create mode 100644 src/test/java/org/alfresco/util/exec/RuntimeExecTest.java create mode 100644 src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java create mode 100644 src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java create mode 100644 src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java create mode 100644 src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java create mode 100644 src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml create mode 100644 src/test/resources/config-areas.xml create mode 100644 src/test/resources/config-multi.xml create mode 100644 src/test/resources/config-props.properties create mode 100644 src/test/resources/config-props.xml create mode 100644 src/test/resources/config-replace.xml create mode 100644 src/test/resources/config.xml create mode 100644 src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml create mode 100644 src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml create mode 100644 src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml create mode 100644 src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml create mode 100644 src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml create mode 100644 src/test/resources/org/alfresco/i18n/testMessages.properties create mode 100644 src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties create mode 100644 src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml create mode 100644 src/test/resources/resource-loader/java.util.AbstractCollection/file.txt create mode 100644 src/test/resources/resource-loader/java.util.AbstractList/file.txt create mode 100644 src/test/resources/resource-loader/java.util.TreeSet/file.txt create mode 100644 src/test/resources/test-config-forms-basic-override.xml create mode 100644 src/test/resources/test-config-forms-basic.xml create mode 100644 src/test/resources/test-config-forms-negative.xml create mode 100644 src/test/resources/test-config-forms.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000..ca086f3f5d --- /dev/null +++ b/pom.xml @@ -0,0 +1,233 @@ + + 4.0.0 + + + org.alfresco + alfresco-super-pom + 6 + + alfresco-core + 6.5-SNAPSHOT + Alfresco Core + Alfresco core libraries and utils + + + scm:svn:https://svn.alfresco.com/repos/alfresco-open-mirror/services/alfresco-core/trunk/ + scm:svn:https://svn.alfresco.com/repos/alfresco-enterprise/services/alfresco-core/trunk/ + + + + + alfresco-internal + https://artifacts.alfresco.com/nexus/content/repositories/releases + + + alfresco-internal-snapshots + https://artifacts.alfresco.com/nexus/content/repositories/snapshots + + + + + 3.2.16.RELEASE + 6.8 + + + + + commons-codec + commons-codec + 1.10 + + + commons-collections + commons-collections + 3.2.2 + + + commons-httpclient + commons-httpclient + 3.1-HTTPCLIENT-1265 + + + commons-logging + commons-logging + 1.2 + + + commons-io + commons-io + 2.4 + + + org.apache.commons + commons-math3 + 3.6.1 + + + org.hibernate + hibernate + 3.2.6-alf-20131023 + + + org.safehaus.jug + jug + 2.0.0 + asl + + + org.mybatis + mybatis + 3.3.0 + + + org.mybatis + mybatis-spring + 1.2.5 + + + org.mybatis + mybatis + + + + + log4j + log4j + 1.2.17 + + + org.json + json + 20160212 + + + org.springframework + spring-orm + ${dependency.spring.version} + + + org.springframework + spring-context + ${dependency.spring.version} + + + org.springframework + spring-context-support + ${dependency.spring.version} + + + org.quartz-scheduler + quartz + 1.8.3-alfresco-patched + + + org.alfresco.surf + spring-surf-core-configservice + ${dependency.surf.version} + + + com.sun.xml.bind + jaxb-xjc + 2.2.7 + + + com.sun.xml.bind + jaxb-impl + 2.2.7 + + + dom4j + dom4j + 1.6.1 + + + org.codehaus.guessencoding + guessencoding + 1.4 + + + javax.transaction + jta + 1.0.1b + + + joda-time + joda-time + 2.9.3 + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + + + javax.servlet + servlet-api + 2.5 + provided + + + + + org.slf4j + slf4j-log4j12 + 1.7.21 + test + + + org.springframework + spring-test + ${dependency.spring.version} + test + + + junit + junit + 4.12 + test + + + org.mockito + mockito-all + 1.10.19 + test + + + commons-dbcp + commons-dbcp + 1.4-DBCP330 + test + + + + + + + + maven-release-plugin + + true + @{project.version} + + + + + + + + maven-jar-plugin + 2.6 + + + + test-jar + + + + + + + + diff --git a/src/main/java/org/alfresco/api/AlfrescoPublicApi.java b/src/main/java/org/alfresco/api/AlfrescoPublicApi.java new file mode 100644 index 0000000000..c3f66179a8 --- /dev/null +++ b/src/main/java/org/alfresco/api/AlfrescoPublicApi.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.api; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used to denote a class or method as part + * of the public API. When a class or method is so designated then + * we will not change it within a release in a way that would make + * it no longer backwardly compatible with an earlier version within + * the release. + * + * @author Greg Melahn + */ +@Target( {ElementType.TYPE,ElementType.METHOD} ) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AlfrescoPublicApi +{ +} diff --git a/src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java b/src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java new file mode 100644 index 0000000000..b3d1ff7622 --- /dev/null +++ b/src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Enumeration; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.DefaultPropertiesPersister; +import org.springframework.util.StringUtils; + +/** + * Simple extension to the{@link DefaultPropertiesPersister} to strip trailing whitespace + * from incoming properties. + * + * @author shane frensley + * @see org.springframework.util.DefaultPropertiesPersister + */ +public class AlfrescoPropertiesPersister extends DefaultPropertiesPersister +{ + + private static Log logger = LogFactory.getLog(AlfrescoPropertiesPersister.class); + + @Override + public void load(Properties props, InputStream is) throws IOException + { + super.load(props, is); + strip(props); + } + + @Override + public void load(Properties props, Reader reader) throws IOException + { + super.load(props, reader); + strip(props); + } + + public void loadFromXml(Properties props, InputStream is) throws IOException + { + super.loadFromXml(props, is); + strip(props); + } + + private void strip(Properties props) + { + for (Enumeration keys = props.keys(); keys.hasMoreElements();) + { + String key = (String) keys.nextElement(); + String val = StringUtils.trimTrailingWhitespace(props.getProperty(key)); + if (logger.isTraceEnabled()) + { + logger.trace("Trimmed trailing whitespace for property " + key + " = " + val); + } + props.setProperty(key, val); + } + } +} diff --git a/src/main/java/org/alfresco/config/JndiObjectFactoryBean.java b/src/main/java/org/alfresco/config/JndiObjectFactoryBean.java new file mode 100644 index 0000000000..9a2a90205d --- /dev/null +++ b/src/main/java/org/alfresco/config/JndiObjectFactoryBean.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.sql.Connection; + +import javax.naming.NamingException; +import javax.sql.DataSource; + +/** + * An extended version of JndiObjectFactoryBean that actually tests a JNDI data source before falling back to its + * default object. Allows continued backward compatibility with old-style datasource configuration. + * + * @author dward + */ +public class JndiObjectFactoryBean extends org.springframework.jndi.JndiObjectFactoryBean +{ + + @Override + protected Object lookup() throws NamingException + { + Object candidate = super.lookup(); + if (candidate instanceof DataSource) + { + Connection con = null; + try + { + con = ((DataSource) candidate).getConnection(); + } + catch (Exception e) + { + NamingException e1 = new NamingException("Unable to get connection from " + getJndiName()); + e1.setRootCause(e); + throw e1; + } + finally + { + try + { + if (con != null) + { + con.close(); + } + } + catch (Exception e) + { + } + } + } + return candidate; + } +} diff --git a/src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java b/src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java new file mode 100644 index 0000000000..70453690e0 --- /dev/null +++ b/src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.util.Properties; + +import javax.naming.NamingException; + +import org.springframework.jndi.JndiTemplate; + +/** + * An extended {@link SystemPropertiesFactoryBean} that allows properties to be set through JNDI entries in + * java:comp/env/properties/*. The precedence given to system properties is still as per the superclass. + * + * @author dward + */ +public class JndiPropertiesFactoryBean extends SystemPropertiesFactoryBean +{ + private JndiTemplate jndiTemplate = new JndiTemplate(); + + @Override + protected void resolveMergedProperty(String propertyName, Properties props) + { + try + { + Object value = this.jndiTemplate.lookup("java:comp/env/properties/" + propertyName); + if (value != null) + { + String stringValue = value.toString(); + if (stringValue.length() > 0) + { + // Unfortunately, JBoss 4 wrongly expects every env-entry declared in web.xml to have an + // env-entry-value (even though these are meant to be decided on deployment!). So we treat the empty + // string as null. + props.setProperty(propertyName, stringValue); + } + } + } + catch (NamingException e) + { + // Fall back to merged value in props + } + } +} diff --git a/src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java b/src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java new file mode 100644 index 0000000000..b775ad0110 --- /dev/null +++ b/src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.util.Properties; + +import javax.naming.NamingException; + +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; +import org.springframework.jndi.JndiTemplate; + +/** + * An extended {@link PropertyPlaceholderConfigurer} that allows properties to be set through JNDI entries in + * java:comp/env/properties/*. The precedence given to system properties is still as per the superclass. + * + * @author dward + */ +public class JndiPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer +{ + private JndiTemplate jndiTemplate = new JndiTemplate(); + + @Override + protected String resolvePlaceholder(String placeholder, Properties props) + { + String result = null; + try + { + Object value = this.jndiTemplate.lookup("java:comp/env/properties/" + placeholder); + if (value != null) + { + result = value.toString(); + } + } + catch (NamingException e) + { + } + // Unfortunately, JBoss 4 wrongly expects every env-entry declared in web.xml to have an env-entry-value (even + // though these are meant to be decided on deployment!). So we treat the empty string as null. + return result == null || result.length() == 0 ? super.resolvePlaceholder(placeholder, props) : result; + } +} diff --git a/src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java b/src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java new file mode 100644 index 0000000000..b7d3f33720 --- /dev/null +++ b/src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.springframework.aop.target.AbstractBeanFactoryBasedTargetSource; +import org.springframework.beans.BeansException; + +/** + * A non-blocking version of LazyInitTargetSource. + * + * @author dward + */ +public class NonBlockingLazyInitTargetSource extends AbstractBeanFactoryBasedTargetSource +{ + + private static final long serialVersionUID = 4509578245779492037L; + private Object target; + private ReadWriteLock lock = new ReentrantReadWriteLock(); + + public Object getTarget() throws BeansException + { + this.lock.readLock().lock(); + try + { + if (this.target != null) + { + return this.target; + } + } + finally + { + this.lock.readLock().unlock(); + } + this.lock.writeLock().lock(); + try + { + if (this.target == null) + { + this.target = getBeanFactory().getBean(getTargetBeanName()); + } + return this.target; + } + finally + { + this.lock.writeLock().unlock(); + } + } +} diff --git a/src/main/java/org/alfresco/config/PathMatchingHelper.java b/src/main/java/org/alfresco/config/PathMatchingHelper.java new file mode 100644 index 0000000000..d0f1c09ab6 --- /dev/null +++ b/src/main/java/org/alfresco/config/PathMatchingHelper.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.io.IOException; +import java.net.URL; +import java.util.Set; + +import org.springframework.core.io.Resource; +import org.springframework.util.PathMatcher; + +/** + * An interface for plug ins to JBossEnabledResourcePatternResolver that avoids direct dependencies on + * application server specifics. + * + * @author dward + */ +public interface PathMatchingHelper +{ + /** + * Indicates whether this helper is capable of searching the given URL (i.e. its protocol is supported). + * + * @param rootURL + * the root url to be searched + * @return true if this helper is capable of searching the given URL + */ + public boolean canHandle(URL rootURL); + + /** + * Gets the resource at the given URL. + * + * @param url URL + * @return the resource at the given URL + * @throws IOException + * for any error + */ + public Resource getResource(URL url) throws IOException; + + /** + * Gets the set of resources under the given URL whose path matches the given sub pattern. + * + * @param matcher + * the matcher + * @param rootURL + * the root URL to be searched + * @param subPattern + * the ant-style pattern to match + * @return the set of matching resources + * @throws IOException + * for any error + */ + public Set getResources(PathMatcher matcher, URL rootURL, String subPattern) throws IOException; +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java b/src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java new file mode 100644 index 0000000000..f9ebb16658 --- /dev/null +++ b/src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.springframework.beans.factory.config.PropertiesFactoryBean; +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; +import org.springframework.core.Constants; + +/** + * Like the parent PropertiesFactoryBean, but overrides or augments the resulting property set with values + * from VM system properties. As with the Spring {@link PropertyPlaceholderConfigurer} the following modes are + * supported: + *
    + *
  • SYSTEM_PROPERTIES_MODE_NEVER: Don't use system properties at all.
  • + *
  • SYSTEM_PROPERTIES_MODE_FALLBACK: Fallback to a system property only for undefined properties.
  • + *
  • SYSTEM_PROPERTIES_MODE_OVERRIDE: (DEFAULT)Use a system property if it is available.
  • + *
+ * Note that system properties will only be included in the property set if defaults for the property have already been + * defined using {@link #setProperties(Properties)} or {@link #setLocations(org.springframework.core.io.Resource[])} or + * their names have been included explicitly in the set passed to {@link #setSystemProperties(Set)}. + * + * @author Derek Hulley + */ +public class SystemPropertiesFactoryBean extends PropertiesFactoryBean +{ + private static final Constants constants = new Constants(PropertyPlaceholderConfigurer.class); + + private int systemPropertiesMode = PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE; + private Set systemProperties = Collections.emptySet(); + + /** + * Set the system property mode by the name of the corresponding constant, e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE". + * + * @param constantName + * name of the constant + * @throws java.lang.IllegalArgumentException + * if an invalid constant was specified + * @see #setSystemPropertiesMode + */ + public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException + { + this.systemPropertiesMode = SystemPropertiesFactoryBean.constants.asNumber(constantName).intValue(); + } + + /** + * Set how to check system properties. + * + * @see PropertyPlaceholderConfigurer#setSystemPropertiesMode(int) + */ + public void setSystemPropertiesMode(int systemPropertiesMode) + { + this.systemPropertiesMode = systemPropertiesMode; + } + + /** + * Set the names of the properties that can be considered for overriding. + * + * @param systemProperties + * a set of properties that can be fetched from the system properties + */ + public void setSystemProperties(Set systemProperties) + { + this.systemProperties = systemProperties; + } + + @SuppressWarnings("unchecked") + @Override + protected Properties mergeProperties() throws IOException + { + // First do the default merge + Properties props = super.mergeProperties(); + + // Now resolve all the merged properties + if (this.systemPropertiesMode == PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_NEVER) + { + // If we are in never mode, we don't refer to system properties at all + for (String systemProperty : (Set) (Set) props.keySet()) + { + resolveMergedProperty(systemProperty, props); + } + } + else + { + // Otherwise, we allow unset properties to drift through from the systemProperties set and potentially set + // ones to be overriden by system properties + Set propNames = new HashSet((Set) (Set) props.keySet()); + propNames.addAll(this.systemProperties); + for (String systemProperty : propNames) + { + resolveMergedProperty(systemProperty, props); + if (this.systemPropertiesMode == PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_FALLBACK + && props.containsKey(systemProperty)) + { + // It's already there + continue; + } + // Get the system value and assign if present + String systemPropertyValue = System.getProperty(systemProperty); + if (systemPropertyValue != null) + { + props.put(systemProperty, systemPropertyValue); + } + } + } + return props; + } + + /** + * Override hook. Allows subclasses to resolve a merged property from an alternative source, whilst still respecting + * the chosen system property fallback path. + * + * @param systemProperty String + * @param props Properties + */ + protected void resolveMergedProperty(String systemProperty, Properties props) + { + } +} diff --git a/src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java b/src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java new file mode 100644 index 0000000000..a00fe869d5 --- /dev/null +++ b/src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Takes a set of properties and pushes them into the Java environment. Usually, VM properties + * are required by the system (see {@link SystemPropertiesFactoryBean} and + * Spring's PropertyPlaceholderConfigurer); sometimes it is necessary to take properties + * available to Spring and push them onto the VM. + *

+ * For simplicity, the system property, if present, will NOT be modified. Also, property placeholders + * (${...}), empty values and null values will be ignored. + *

+ * Use the {@link #init()} method to push the properties. + * + * @author Derek Hulley + * @since V3.1 + */ +public class SystemPropertiesSetterBean +{ + private static Log logger = LogFactory.getLog(SystemPropertiesSetterBean.class); + + private Map propertyMap; + + SystemPropertiesSetterBean() + { + propertyMap = new HashMap(3); + } + + /** + * Set the properties that will be pushed onto the JVM. + * + * @param propertyMap a map of property name to property value + */ + public void setPropertyMap(Map propertyMap) + { + this.propertyMap = propertyMap; + } + + public void init() + { + for (Map.Entry entry : propertyMap.entrySet()) + { + String name = entry.getKey(); + String value = entry.getValue(); + // Some values can be ignored + if (value == null || value.length() == 0) + { + continue; + } + if (value.startsWith("${") && value.endsWith("}")) + { + continue; + } + // Check the system properties + if (System.getProperty(name) != null) + { + // It was already there + if (logger.isDebugEnabled()) + { + logger.debug("\n" + + "Not pushing up system property: \n" + + " Property: " + name + "\n" + + " Value already present: " + System.getProperty(name) + "\n" + + " Value provided: " + value); + } + continue; + } + System.setProperty(name, value); + } + } +} diff --git a/src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java b/src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java new file mode 100644 index 0000000000..cceb32c944 --- /dev/null +++ b/src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encoding; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Arrays; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @since 2.1 + * @author Derek Hulley + */ +public abstract class AbstractCharactersetFinder implements CharactersetFinder +{ + private static Log logger = LogFactory.getLog(AbstractCharactersetFinder.class); + private static boolean isDebugEnabled = logger.isDebugEnabled(); + + private int bufferSize; + + public AbstractCharactersetFinder() + { + this.bufferSize = 8192; + } + + /** + * Set the maximum number of bytes to read ahead when attempting to determine the characterset. + * Most characterset detectors are efficient and can process 8K of buffered data very quickly. + * Some, may need to be constrained a bit. + * + * @param bufferSize the number of bytes - default 8K. + */ + public void setBufferSize(int bufferSize) + { + this.bufferSize = bufferSize; + } + + /** + * {@inheritDoc} + *

+ * The input stream is checked to ensure that it supports marks, after which + * a buffer is extracted, leaving the stream in its original state. + */ + public final Charset detectCharset(InputStream is) + { + // Only support marking streams + if (!is.markSupported()) + { + throw new IllegalArgumentException("The InputStream must support marks. Wrap the stream in a BufferedInputStream."); + } + try + { + int bufferSize = getBufferSize(); + if (bufferSize < 0) + { + throw new RuntimeException("The required buffer size may not be negative: " + bufferSize); + } + // Mark the stream for just a few more than we actually will need + is.mark(bufferSize); + // Create a buffer to hold the data + byte[] buffer = new byte[bufferSize]; + // Fill it + int read = is.read(buffer); + // Create an appropriately sized buffer + if (read > -1 && read < buffer.length) + { + byte[] copyBuffer = new byte[read]; + System.arraycopy(buffer, 0, copyBuffer, 0, read); + buffer = copyBuffer; + } + // Detect + return detectCharset(buffer); + } + catch (IOException e) + { + // Attempt a reset + throw new AlfrescoRuntimeException("IOException while attempting to detect charset encoding.", e); + } + finally + { + try { is.reset(); } catch (Throwable ee) {} + } + } + + public final Charset detectCharset(byte[] buffer) + { + try + { + Charset charset = detectCharsetImpl(buffer); + // Done + if (isDebugEnabled) + { + if (charset == null) + { + // Read a few characters for debug purposes + logger.debug("\n" + + "Failed to identify stream character set: \n" + + " Guessed 'chars': " + Arrays.toString(buffer)); + } + else + { + // Read a few characters for debug purposes + logger.debug("\n" + + "Identified character set from stream:\n" + + " Charset: " + charset + "\n" + + " Detected chars: " + new String(buffer, charset.name())); + } + } + return charset; + } + catch (Throwable e) + { + logger.error("IOException while attempting to detect charset encoding.", e); + return null; + } + } + + /** + * Some implementations may only require a few bytes to do detect the stream type, + * whilst others may be more efficient with larger buffers. In either case, the + * number of bytes actually present in the buffer cannot be enforced. + *

+ * Only override this method if there is a very compelling reason to adjust the buffer + * size, and then consider handling the {@link #setBufferSize(int)} method by issuing a + * warning. This will prevent users from setting the buffer size when it has no effect. + * + * @return Returns the maximum desired size of the buffer passed + * to the {@link CharactersetFinder#detectCharset(byte[])} method. + * + * @see #setBufferSize(int) + */ + protected int getBufferSize() + { + return bufferSize; + } + + /** + * Worker method for implementations to override. All exceptions will be reported and + * absorbed and null returned. + *

+ * The interface contract is that the data buffer must not be altered in any way. + * + * @param buffer the buffer of data no bigger than the requested + * {@linkplain #getBufferSize() best buffer size}. This can, + * very efficiently, be turned into an InputStream using a + * ByteArrayInputStream. + * @return Returns the charset or null if an accurate conclusion + * is not possible + * @throws Exception Any exception, checked or not + */ + protected abstract Charset detectCharsetImpl(byte[] buffer) throws Exception; +} diff --git a/src/main/java/org/alfresco/encoding/BomCharactersetFinder.java b/src/main/java/org/alfresco/encoding/BomCharactersetFinder.java new file mode 100644 index 0000000000..d4908c0320 --- /dev/null +++ b/src/main/java/org/alfresco/encoding/BomCharactersetFinder.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encoding; + +import java.io.ByteArrayInputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Byte Order Marker encoding detection. + * + * @since 2.1 + * @author Pacific Northwest National Lab + * @author Derek Hulley + */ +public class BomCharactersetFinder extends AbstractCharactersetFinder +{ + private static Log logger = LogFactory.getLog(BomCharactersetFinder.class); + + @Override + public void setBufferSize(int bufferSize) + { + logger.warn("Setting the buffersize has no effect for charset finder: " + BomCharactersetFinder.class.getName()); + } + + /** + * @return Returns 64 + */ + @Override + protected int getBufferSize() + { + return 64; + } + + /** + * Just searches the Byte Order Marker, i.e. the first three characters for a sign of + * the encoding. + */ + protected Charset detectCharsetImpl(byte[] buffer) throws Exception + { + Charset charset = null; + ByteArrayInputStream bis = null; + try + { + bis = new ByteArrayInputStream(buffer); + bis.mark(3); + char[] byteHeader = new char[3]; + InputStreamReader in = new InputStreamReader(bis); + int bytesRead = in.read(byteHeader); + bis.reset(); + + if (bytesRead < 2) + { + // ASCII + charset = Charset.forName("Cp1252"); + } + else if ( + byteHeader[0] == 0xFE && + byteHeader[1] == 0xFF) + { + // UCS-2 Big Endian + charset = Charset.forName("UTF-16BE"); + } + else if ( + byteHeader[0] == 0xFF && + byteHeader[1] == 0xFE) + { + // UCS-2 Little Endian + charset = Charset.forName("UTF-16LE"); + } + else if ( + bytesRead >= 3 && + byteHeader[0] == 0xEF && + byteHeader[1] == 0xBB && + byteHeader[2] == 0xBF) + { + // UTF-8 + charset = Charset.forName("UTF-8"); + } + else + { + // No idea + charset = null; + } + // Done + return charset; + } + finally + { + if (bis != null) + { + try { bis.close(); } catch (Throwable e) {} + } + } + } +} diff --git a/src/main/java/org/alfresco/encoding/CharactersetFinder.java b/src/main/java/org/alfresco/encoding/CharactersetFinder.java new file mode 100644 index 0000000000..365b55bcf0 --- /dev/null +++ b/src/main/java/org/alfresco/encoding/CharactersetFinder.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encoding; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.nio.charset.Charset; + +/** + * Interface for classes that are able to read a text-based input stream and determine + * the character encoding. + *

+ * There are quite a few libraries that do this, but none are perfect. It is therefore + * necessary to abstract the implementation to allow these finders to be configured in + * as required. + *

+ * Implementations should have a default constructor and be completely thread safe and + * stateless. This will allow them to be constructed and held indefinitely to do the + * decoding work. + *

+ * Where the encoding cannot be determined, it is left to the client to decide what to do. + * Some implementations may guess and encoding or use a default guess - it is up to the + * implementation to specify the behaviour. + * + * @since 2.1 + * @author Derek Hulley + */ +public interface CharactersetFinder +{ + /** + * Attempt to detect the character set encoding for the give input stream. The input + * stream will not be altered or closed by this method, and must therefore support + * marking. If the input stream available doesn't support marking, then it can be wrapped with + * a {@link BufferedInputStream}. + *

+ * The current state of the stream will be restored before the method returns. + * + * @param is an input stream that must support marking + * @return Returns the encoding of the stream, + * or null if encoding cannot be identified + */ + public Charset detectCharset(InputStream is); + + /** + * Attempt to detect the character set encoding for the given buffer. + * + * @param buffer the first n bytes of the character stream + * @return Returns the encoding of the buffer, + * or null if encoding cannot be identified + */ + public Charset detectCharset(byte[] buffer); +} diff --git a/src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java b/src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java new file mode 100644 index 0000000000..92cd35f1c6 --- /dev/null +++ b/src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encoding; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import com.glaforge.i18n.io.CharsetToolkit; + +/** + * Uses the Guess Encoding + * library. + * + * @since 2.1 + * @author Derek Hulley + */ +public class GuessEncodingCharsetFinder extends AbstractCharactersetFinder +{ + /** Dummy charset to detect the default guess */ + private static final Charset DUMMY_CHARSET = new DummyCharset(); + + @Override + protected Charset detectCharsetImpl(byte[] buffer) throws Exception + { + CharsetToolkit charsetToolkit = new CharsetToolkit(buffer, DUMMY_CHARSET); + charsetToolkit.setEnforce8Bit(true); // Force the default instead of a guess + Charset charset = charsetToolkit.guessEncoding(); + if (charset == DUMMY_CHARSET) + { + return null; + } + else + { + return charset; + } + } + + /** + * A dummy charset to detect a default hit. + */ + public static class DummyCharset extends Charset + { + DummyCharset() + { + super("dummy", new String[] {}); + } + + @Override + public boolean contains(Charset cs) + { + throw new UnsupportedOperationException(); + } + + @Override + public CharsetDecoder newDecoder() + { + throw new UnsupportedOperationException(); + } + + @Override + public CharsetEncoder newEncoder() + { + throw new UnsupportedOperationException(); + } + + } +} diff --git a/src/main/java/org/alfresco/encryption/AbstractEncryptor.java b/src/main/java/org/alfresco/encryption/AbstractEncryptor.java new file mode 100644 index 0000000000..865df534a4 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/AbstractEncryptor.java @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.Key; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SealedObject; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Basic support for encryption engines. + * + * @since 4.0 + */ +public abstract class AbstractEncryptor implements Encryptor +{ + protected static final Log logger = LogFactory.getLog(Encryptor.class); + protected String cipherAlgorithm; + protected String cipherProvider; + + protected KeyProvider keyProvider; + + /** + * Constructs with defaults + */ + protected AbstractEncryptor() + { + } + + /** + * @param keyProvider provides encryption keys based on aliases + */ + public void setKeyProvider(KeyProvider keyProvider) + { + this.keyProvider = keyProvider; + } + + public KeyProvider getKeyProvider() + { + return keyProvider; + } + + public void init() + { + PropertyCheck.mandatory(this, "keyProvider", keyProvider); + } + + /** + * Factory method to be written by implementations to construct and initialize + * physical ciphering objects. + * + * @param keyAlias the key alias + * @param params algorithm-specific parameters + * @param mode the cipher mode + * @return Cipher + */ + protected abstract Cipher getCipher(String keyAlias, AlgorithmParameters params, int mode); + + /** + * {@inheritDoc} + */ + @Override + public Pair encrypt(String keyAlias, AlgorithmParameters params, byte[] input) + { + Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE); + if (cipher == null) + { + return new Pair(input, null); + } + try + { + byte[] output = cipher.doFinal(input); + params = cipher.getParameters(); + return new Pair(output, params); + } + catch (Throwable e) + { +// cipher.init(Cipher.ENCRYPT_MODE, key, params); + throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e); + } + } + + protected void resetCipher() + { + + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] decrypt(String keyAlias, AlgorithmParameters params, byte[] input) + { + Cipher cipher = getCipher(keyAlias, params, Cipher.DECRYPT_MODE); + if (cipher == null) + { + return input; + } + try + { + return cipher.doFinal(input); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public InputStream decrypt(String keyAlias, AlgorithmParameters params, InputStream input) + { + Cipher cipher = getCipher(keyAlias, params, Cipher.DECRYPT_MODE); + if (cipher == null) + { + return input; + } + + try + { + return new CipherInputStream(input, cipher); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException("Decryption failed for key alias: " + keyAlias, e); + } + } + + /** + * {@inheritDoc} + *

+ * Serializes and {@link #encrypt(String, AlgorithmParameters, byte[]) encrypts} the input data. + */ + @Override + public Pair encryptObject(String keyAlias, AlgorithmParameters params, Object input) + { + try + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(input); + byte[] unencrypted = bos.toByteArray(); + return encrypt(keyAlias, params, unencrypted); + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to serialize or encrypt object", e); + } + } + + /** + * {@inheritDoc} + *

+ * {@link #decrypt(String, AlgorithmParameters, byte[]) Decrypts} and deserializes the input data + */ + @Override + public Object decryptObject(String keyAlias, AlgorithmParameters params, byte[] input) + { + try + { + byte[] unencrypted = decrypt(keyAlias, params, input); + ByteArrayInputStream bis = new ByteArrayInputStream(unencrypted); + ObjectInputStream ois = new ObjectInputStream(bis); + Object obj = ois.readObject(); + return obj; + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to deserialize or decrypt object", e); + } + } + + @Override + public Serializable sealObject(String keyAlias, AlgorithmParameters params, Serializable input) + { + if (input == null) + { + return null; + } + Cipher cipher = getCipher(keyAlias, params, Cipher.ENCRYPT_MODE); + if (cipher == null) + { + return input; + } + try + { + return new SealedObject(input, cipher); + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to seal object", e); + } + } + + @Override + public Serializable unsealObject(String keyAlias, Serializable input) throws InvalidKeyException + { + if (input == null) + { + return input; + } + // Don't unseal it if it is not sealed + if (!(input instanceof SealedObject)) + { + return input; + } + // Get the Key, rather than a Cipher + Key key = keyProvider.getKey(keyAlias); + if (key == null) + { + // The client will be expecting to unseal the object + throw new IllegalStateException("No key matching " + keyAlias + ". Cannot unseal object."); + } + // Unseal it using the key + SealedObject sealedInput = (SealedObject) input; + try + { + Serializable output = (Serializable) sealedInput.getObject(key); + // Done + return output; + } + catch(InvalidKeyException e) + { + // let these through, can be useful to client code to know this is the cause + throw e; + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to unseal object", e); + } + } + + public void setCipherAlgorithm(String cipherAlgorithm) + { + this.cipherAlgorithm = cipherAlgorithm; + } + + public String getCipherAlgorithm() + { + return this.cipherAlgorithm; + } + + public void setCipherProvider(String cipherProvider) + { + this.cipherProvider = cipherProvider; + } + + public String getCipherProvider() + { + return this.cipherProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public AlgorithmParameters decodeAlgorithmParameters(byte[] encoded) + { + try + { + AlgorithmParameters p = null; + String algorithm = "DESede"; + if(getCipherProvider() != null) + { + p = AlgorithmParameters.getInstance(algorithm, getCipherProvider()); + } + else + { + p = AlgorithmParameters.getInstance(algorithm); + } + p.init(encoded); + return p; + } + catch(Exception e) + { + throw new AlfrescoRuntimeException("", e); + } + } +} diff --git a/src/main/java/org/alfresco/encryption/AbstractKeyProvider.java b/src/main/java/org/alfresco/encryption/AbstractKeyProvider.java new file mode 100644 index 0000000000..297f28c355 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/AbstractKeyProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +/** + * Basic support for key providers + *

+ * TODO: This class will provide the alias name mapping so that use-cases can be mapped + * to different alias names in the keystore. + * + * @author Derek Hulley + * @since 4.0 + */ +public abstract class AbstractKeyProvider implements KeyProvider +{ + /* + * Not a useless class. + */ +} diff --git a/src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java b/src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java new file mode 100644 index 0000000000..a017ce984a --- /dev/null +++ b/src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.security.Key; +import java.util.Set; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; + +/** + * Manages a Java Keystore for Alfresco, including caching keys where appropriate. + * + * @since 4.0 + * + */ +public interface AlfrescoKeyStore +{ + public static final String KEY_KEYSTORE_PASSWORD = "keystore.password"; + + /** + * The name of the keystore. + * + * @return the name of the keystore. + */ + public String getName(); + + /** + * Backup the keystore to the backup location. Write the keys to the backup keystore. + */ + public void backup(); + + /** + * The key store parameters. + * + * @return KeyStoreParameters + */ + public KeyStoreParameters getKeyStoreParameters(); + + /** + * The backup key store parameters. + * + * @return * @return + + */ + public KeyStoreParameters getBackupKeyStoreParameters(); + + /** + * Does the underlying key store exist? + * + * @return true if it exists, false otherwise + */ + public boolean exists(); + + /** + * Return the key with the given key alias. + * + * @param keyAlias String + * @return Key + */ + public Key getKey(String keyAlias); + + /** + * Return the timestamp (in ms) of when the key was last loaded from the keystore on disk. + * + * @param keyAlias String + * @return long + */ + public long getKeyTimestamp(String keyAlias); + + /** + * Return the backup key with the given key alias. + * + * @param keyAlias String + * @return Key + */ + public Key getBackupKey(String keyAlias); + + /** + * Return all key aliases in the key store. + * + * @return Set + */ + public Set getKeyAliases(); + + /** + * Create an array of key managers from keys in the key store. + * + * @return KeyManager[] + */ + public KeyManager[] createKeyManagers(); + + /** + * Create an array of trust managers from certificates in the key store. + * + * @return TrustManager[] + */ + public TrustManager[] createTrustManagers(); + + /** + * Create the key store if it doesn't exist. + * A key for each key alias will be written to the keystore on disk, either from the cached keys or, if not present, a key will be generated. + */ + public void create(); + + /** + * Reload the keys from the key store. + */ + public void reload() throws InvalidKeystoreException, MissingKeyException; + + /** + * Check that the keys in the key store are valid i.e. that they match those registered. + */ + public void validateKeys() throws InvalidKeystoreException, MissingKeyException; + +} diff --git a/src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java b/src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java new file mode 100644 index 0000000000..085bec231a --- /dev/null +++ b/src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java @@ -0,0 +1,1100 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.management.openmbean.KeyAlreadyExistsException; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import org.alfresco.encryption.EncryptionKeysRegistry.KEY_STATUS; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This wraps a Java Keystore and caches the encryption keys. It manages the loading and caching of the encryption keys + * and their registration with and validation against the encryption key registry. + * + * @since 4.0 + * + */ +public class AlfrescoKeyStoreImpl implements AlfrescoKeyStore +{ + private static final Log logger = LogFactory.getLog(AlfrescoKeyStoreImpl.class); + + protected KeyStoreParameters keyStoreParameters; + protected KeyStoreParameters backupKeyStoreParameters; + protected KeyResourceLoader keyResourceLoader; + protected EncryptionKeysRegistry encryptionKeysRegistry; + + protected KeyMap keys; + protected KeyMap backupKeys; + protected final WriteLock writeLock; + protected final ReadLock readLock; + + private Set keysToValidate; + protected boolean validateKeyChanges = false; + + public AlfrescoKeyStoreImpl() + { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + writeLock = lock.writeLock(); + readLock = lock.readLock(); + this.keys = new KeyMap(); + this.backupKeys = new KeyMap(); + } + + public AlfrescoKeyStoreImpl(KeyStoreParameters keyStoreParameters, KeyResourceLoader keyResourceLoader) + { + this(); + + this.keyResourceLoader = keyResourceLoader; + this.keyStoreParameters = keyStoreParameters; + + safeInit(); + } + + public void init() + { + writeLock.lock(); + try + { + safeInit(); + } + finally + { + writeLock.unlock(); + } + } + + public void setEncryptionKeysRegistry( + EncryptionKeysRegistry encryptionKeysRegistry) + { + this.encryptionKeysRegistry = encryptionKeysRegistry; + } + + public void setValidateKeyChanges(boolean validateKeyChanges) + { + this.validateKeyChanges = validateKeyChanges; + } + + public void setKeysToValidate(Set keysToValidate) + { + this.keysToValidate = keysToValidate; + } + + public void setKeyStoreParameters(KeyStoreParameters keyStoreParameters) + { + this.keyStoreParameters = keyStoreParameters; + } + + public void setBackupKeyStoreParameters( + KeyStoreParameters backupKeyStoreParameters) + { + this.backupKeyStoreParameters = backupKeyStoreParameters; + } + + public void setKeyResourceLoader(KeyResourceLoader keyResourceLoader) + { + this.keyResourceLoader = keyResourceLoader; + } + + public KeyStoreParameters getKeyStoreParameters() + { + return keyStoreParameters; + } + + public KeyStoreParameters getBackupKeyStoreParameters() + { + return backupKeyStoreParameters; + } + + public KeyResourceLoader getKeyResourceLoader() + { + return keyResourceLoader; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() + { + return keyStoreParameters.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public void validateKeys() throws InvalidKeystoreException, MissingKeyException + { + validateKeys(keys, backupKeys); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean exists() + { + return keyStoreExists(getKeyStoreParameters().getLocation()); + } + + /** + * {@inheritDoc} + */ + @Override + public void reload() throws InvalidKeystoreException, MissingKeyException + { + KeyMap keys = loadKeyStore(getKeyStoreParameters()); + KeyMap backupKeys = loadKeyStore(getBackupKeyStoreParameters()); + + validateKeys(keys, backupKeys); + + // all ok, reload the keys + writeLock.lock(); + try + { + this.keys = keys; + this.backupKeys = backupKeys; + } + finally + { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Set getKeyAliases() + { + return new HashSet(keys.getKeyAliases()); + } + + /** + * {@inheritDoc} + */ + @Override + public void backup() + { + writeLock.lock(); + try + { + for(String keyAlias : keys.getKeyAliases()) + { + backupKeys.setKey(keyAlias, keys.getKey(keyAlias)); + } + createKeyStore(backupKeyStoreParameters, backupKeys); + } + finally + { + writeLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void create() + { + createKeyStore(keyStoreParameters, keys); + } + + /** + * {@inheritDoc} + */ + @Override + public Key getKey(String keyAlias) + { + readLock.lock(); + try + { + return keys.getCachedKey(keyAlias).getKey(); + } + finally + { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public long getKeyTimestamp(String keyAlias) + { + readLock.lock(); + try + { + CachedKey cachedKey = keys.getCachedKey(keyAlias); + return cachedKey.getTimestamp(); + } + finally + { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Key getBackupKey(String keyAlias) + { + readLock.lock(); + try + { + return backupKeys.getCachedKey(keyAlias).getKey(); + } + finally + { + readLock.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public KeyManager[] createKeyManagers() + { + KeyInfoManager keyInfoManager = null; + + try + { + keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation()); + KeyStore ks = loadKeyStore(keyStoreParameters, keyInfoManager); + + logger.debug("Initializing key managers"); + KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + + String keyStorePassword = keyInfoManager.getKeyStorePassword(); + kmfactory.init(ks, keyStorePassword != null ? keyStorePassword.toCharArray(): null); + return kmfactory.getKeyManagers(); + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException("Unable to create key manager", e); + } + finally + { + if(keyInfoManager != null) + { + keyInfoManager.clear(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public TrustManager[] createTrustManagers() + { + KeyInfoManager keyInfoManager = null; + + try + { + keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation()); + KeyStore ks = loadKeyStore(getKeyStoreParameters(), keyInfoManager); + + logger.debug("Initializing trust managers"); + TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmfactory.init(ks); + return tmfactory.getTrustManagers(); + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException("Unable to create key manager", e); + } + finally + { + if(keyInfoManager != null) + { + keyInfoManager.clear(); + } + } + } + + protected String getKeyMetaDataFileLocation() + { + return keyStoreParameters.getKeyMetaDataFileLocation(); + } + + protected InputStream getKeyStoreStream(String location) throws FileNotFoundException + { + if(location == null) + { + return null; + } + return keyResourceLoader.getKeyStore(location); + } + + protected OutputStream getKeyStoreOutStream() throws FileNotFoundException + { + return new FileOutputStream(getKeyStoreParameters().getLocation()); + } + + protected KeyInfoManager getKeyInfoManager(String metadataFileLocation) throws FileNotFoundException, IOException + { + return new KeyInfoManager(metadataFileLocation, keyResourceLoader); + } + + protected KeyMap cacheKeys(KeyStore ks, KeyInfoManager keyInfoManager) + throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException + { + KeyMap keys = new KeyMap(); + + // load and cache the keys + for(Entry keyEntry : keyInfoManager.getKeyInfo().entrySet()) + { + String keyAlias = keyEntry.getKey(); + + KeyInformation keyInfo = keyInfoManager.getKeyInformation(keyAlias); + String passwordStr = keyInfo != null ? keyInfo.getPassword() : null; + + // Null is an acceptable value (means no key) + Key key = null; + + // Attempt to get the key + key = ks.getKey(keyAlias, passwordStr == null ? null : passwordStr.toCharArray()); + if(key != null) + { + keys.setKey(keyAlias, key); + } + // Key loaded + if (logger.isDebugEnabled()) + { + logger.debug( + "Retrieved key from keystore: \n" + + " Location: " + getKeyStoreParameters().getLocation() + "\n" + + " Provider: " + getKeyStoreParameters().getProvider() + "\n" + + " Type: " + getKeyStoreParameters().getType() + "\n" + + " Alias: " + keyAlias + "\n" + + " Password?: " + (passwordStr != null)); + + Certificate[] certs = ks.getCertificateChain(keyAlias); + if(certs != null) + { + logger.debug("Certificate chain '" + keyAlias + "':"); + for(int c = 0; c < certs.length; c++) + { + if(certs[c] instanceof X509Certificate) + { + X509Certificate cert = (X509Certificate)certs[c]; + logger.debug(" Certificate " + (c + 1) + ":"); + logger.debug(" Subject DN: " + cert.getSubjectDN()); + logger.debug(" Signature Algorithm: " + cert.getSigAlgName()); + logger.debug(" Valid from: " + cert.getNotBefore() ); + logger.debug(" Valid until: " + cert.getNotAfter()); + logger.debug(" Issuer: " + cert.getIssuerDN()); + } + } + } + } + } + + return keys; + } + + protected KeyStore initialiseKeyStore(String type, String provider) + { + KeyStore ks = null; + + try + { + if(provider == null || provider.equals("")) + { + ks = KeyStore.getInstance(type); + } + else + { + ks = KeyStore.getInstance(type, provider); + } + + ks.load(null, null); + + return ks; + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException("Unable to intialise key store", e); + } + } + + protected KeyStore loadKeyStore(KeyStoreParameters keyStoreParameters, KeyInfoManager keyInfoManager) + { + String pwdKeyStore = null; + + try + { + KeyStore ks = initialiseKeyStore(keyStoreParameters.getType(), keyStoreParameters.getProvider()); + + // Load it up + InputStream is = getKeyStoreStream(keyStoreParameters.getLocation()); + if (is != null) + { + try + { + // Get the keystore password + pwdKeyStore = keyInfoManager.getKeyStorePassword(); + ks.load(is, pwdKeyStore == null ? null : pwdKeyStore.toCharArray()); + } + finally + { + try {is.close(); } catch (Throwable e) {} + } + } + else + { + // this is ok, the keystore will contain no keys. + logger.warn("Keystore file doesn't exist: " + keyStoreParameters.getLocation()); + } + + return ks; + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException("Unable to load key store: " + keyStoreParameters.getLocation(), e); + } + finally + { + pwdKeyStore = null; + } + } + + /** + * Initializes class + */ + private void safeInit() + { + PropertyCheck.mandatory(this, "location", getKeyStoreParameters().getLocation()); + + // Make sure we choose the default type, if required + if(getKeyStoreParameters().getType() == null) + { + keyStoreParameters.setType(KeyStore.getDefaultType()); + } + + writeLock.lock(); + try + { + keys = loadKeyStore(keyStoreParameters); + backupKeys = loadKeyStore(backupKeyStoreParameters); + } + finally + { + writeLock.unlock(); + } + } + + private KeyMap loadKeyStore(KeyStoreParameters keyStoreParameters) + { + InputStream is = null; + KeyInfoManager keyInfoManager = null; + KeyStore ks = null; + + if(keyStoreParameters == null) + { + // empty key map + return new KeyMap(); + } + + try + { + keyInfoManager = getKeyInfoManager(keyStoreParameters.getKeyMetaDataFileLocation()); + ks = loadKeyStore(keyStoreParameters, keyInfoManager); + // Loaded + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to initialize keystore: \n" + + " Location: " + getKeyStoreParameters().getLocation() + "\n" + + " Provider: " + getKeyStoreParameters().getProvider() + "\n" + + " Type: " + getKeyStoreParameters().getType(), + e); + } + finally + { + if(keyInfoManager != null) + { + keyInfoManager.clearKeyStorePassword(); + } + + if (is != null) + { + try + { + is.close(); + } + catch (Throwable e) + { + + } + } + } + + try + { + // cache the keys from the keystore + KeyMap keys = cacheKeys(ks, keyInfoManager); + + if(logger.isDebugEnabled()) + { + logger.debug( + "Initialized keystore: \n" + + " Location: " + getKeyStoreParameters().getLocation() + "\n" + + " Provider: " + getKeyStoreParameters().getProvider() + "\n" + + " Type: " + getKeyStoreParameters().getType() + "\n" + + keys.numKeys() + " keys found"); + } + + return keys; + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to retrieve keys from keystore: \n" + + " Location: " + getKeyStoreParameters().getLocation() + "\n" + + " Provider: " + getKeyStoreParameters().getProvider() + "\n" + + " Type: " + getKeyStoreParameters().getType() + "\n", + e); + } + finally + { + // Clear key information + keyInfoManager.clear(); + } + } + + protected void createKey(String keyAlias) + { + KeyInfoManager keyInfoManager = null; + + try + { + keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation()); + Key key = getSecretKey(keyInfoManager.getKeyInformation(keyAlias)); + encryptionKeysRegistry.registerKey(keyAlias, key); + keys.setKey(keyAlias, key); + + KeyStore ks = loadKeyStore(getKeyStoreParameters(), keyInfoManager); + ks.setKeyEntry(keyAlias, key, keyInfoManager.getKeyInformation(keyAlias).getPassword().toCharArray(), null); + OutputStream keyStoreOutStream = getKeyStoreOutStream(); + ks.store(keyStoreOutStream, keyInfoManager.getKeyStorePassword().toCharArray()); + // Workaround for MNT-15005 + keyStoreOutStream.close(); + + logger.info("Created key: " + keyAlias + "\n in key store: \n" + + " Location: " + getKeyStoreParameters().getLocation() + "\n" + + " Provider: " + getKeyStoreParameters().getProvider() + "\n" + + " Type: " + getKeyStoreParameters().getType()); + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to create key: " + keyAlias + "\n in key store: \n" + + " Location: " + getKeyStoreParameters().getLocation() + "\n" + + " Provider: " + getKeyStoreParameters().getProvider() + "\n" + + " Type: " + getKeyStoreParameters().getType(), + e); + } + finally + { + if(keyInfoManager != null) + { + keyInfoManager.clear(); + } + } + } + + protected void createKeyStore(KeyStoreParameters keyStoreParameters, KeyMap keys) + { + KeyInfoManager keyInfoManager = null; + + try + { + if(!keyStoreExists(keyStoreParameters.getLocation())) + { + keyInfoManager = getKeyInfoManager(keyStoreParameters.getKeyMetaDataFileLocation()); + KeyStore ks = initialiseKeyStore(keyStoreParameters.getType(), keyStoreParameters.getProvider()); + + String keyStorePassword = keyInfoManager.getKeyStorePassword(); + if(keyStorePassword == null) + { + throw new AlfrescoRuntimeException("Key store password is null for keystore at location " + + getKeyStoreParameters().getLocation() + + ", key store meta data location" + getKeyMetaDataFileLocation()); + } + + for(String keyAlias : keys.getKeyAliases()) + { + KeyInformation keyInfo = keyInfoManager.getKeyInformation(keyAlias); + + Key key = keys.getKey(keyAlias); + if(key == null) + { + logger.warn("Key with alias " + keyAlias + " is null when creating keystore at location " + keyStoreParameters.getLocation()); + } + else + { + ks.setKeyEntry(keyAlias, key, keyInfo.getPassword().toCharArray(), null); + } + } + +// try +// { +// throw new Exception("Keystore creation: " + ); +// } +// catch(Throwable e) +// { +// logger.debug(e.getMessage()); +// e.printStackTrace(); +// } + + OutputStream keyStoreOutStream = getKeyStoreOutStream(); + ks.store(keyStoreOutStream, keyStorePassword.toCharArray()); + // Workaround for MNT-15005 + keyStoreOutStream.close(); + } + else + { + logger.warn("Can't create key store " + keyStoreParameters.getLocation() + ", already exists."); + } + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to create keystore: \n" + + " Location: " + keyStoreParameters.getLocation() + "\n" + + " Provider: " + keyStoreParameters.getProvider() + "\n" + + " Type: " + keyStoreParameters.getType(), + e); + } + finally + { + if(keyInfoManager != null) + { + keyInfoManager.clear(); + } + } + } + + /* + * For testing + */ +// void createBackup() +// { +// createKeyStore(backupKeyStoreParameters, backupKeys); +// } + + private byte[] generateKeyData() + { + try + { + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + byte bytes[] = new byte[DESedeKeySpec.DES_EDE_KEY_LEN]; + random.nextBytes(bytes); + return bytes; + } + catch(Exception e) + { + throw new RuntimeException("Unable to generate secret key", e); + } + } + + protected Key getSecretKey(KeyInformation keyInformation) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException + { + byte[] keyData = keyInformation.getKeyData(); + + if(keyData == null) + { + if(keyInformation.getKeyAlgorithm().equals("DESede")) + { + // no key data provided, generate key data automatically + keyData = generateKeyData(); + } + else + { + throw new AlfrescoRuntimeException("Unable to generate secret key: key algorithm is not DESede and no keyData provided"); + } + } + + DESedeKeySpec keySpec = new DESedeKeySpec(keyData); + SecretKeyFactory kf = SecretKeyFactory.getInstance(keyInformation.getKeyAlgorithm()); + SecretKey secretKey = kf.generateSecret(keySpec); + return secretKey; + } + + void importPrivateKey(String keyAlias, String keyPassword, InputStream fl, InputStream certstream) + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, CertificateException, KeyStoreException + { + KeyInfoManager keyInfoManager = null; + + writeLock.lock(); + try + { + keyInfoManager = getKeyInfoManager(getKeyMetaDataFileLocation()); + KeyStore ks = loadKeyStore(getKeyStoreParameters(), keyInfoManager); + + // loading Key + byte[] keyBytes = new byte[fl.available()]; + KeyFactory kf = KeyFactory.getInstance("RSA"); + fl.read(keyBytes, 0, fl.available()); + fl.close(); + PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec(keyBytes); + PrivateKey key = kf.generatePrivate(keysp); + + // loading CertificateChain + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + @SuppressWarnings("rawtypes") + Collection c = cf.generateCertificates(certstream) ; + Certificate[] certs = new Certificate[c.toArray().length]; + + certs = (Certificate[])c.toArray(new Certificate[0]); + + // storing keystore + ks.setKeyEntry(keyAlias, key, keyPassword.toCharArray(), certs); + + if(logger.isDebugEnabled()) + { + logger.debug("Key and certificate stored."); + logger.debug("Alias:"+ keyAlias); + } + OutputStream keyStoreOutStream = getKeyStoreOutStream(); + ks.store(keyStoreOutStream, keyPassword.toCharArray()); + // Workaround for MNT-15005 + keyStoreOutStream.close(); + } + finally + { + if(keyInfoManager != null) + { + keyInfoManager.clear(); + } + + writeLock.unlock(); + } + } + + public boolean backupExists() + { + return keyStoreExists(getBackupKeyStoreParameters().getLocation()); + } + + protected boolean keyStoreExists(String location) + { + try + { + InputStream is = getKeyStoreStream(location); + if (is == null) + { + return false; + } + else + { + try { is.close(); } catch (Throwable e) {} + return true; + } + } + catch(FileNotFoundException e) + { + return false; + } + } + + /* + * Validates the keystore keys against the key registry, throwing exceptions if the keys have been unintentionally changed. + * + * For each key to validate: + * + * (i) no main key, no backup key, the key is registered for the main keystore -> error, must re-instate the keystore + * (ii) no main key, no backup key, the key is not registered -> create the main key store and register the key + * (iii) main key exists but is not registered -> register the key + * (iv) main key exists, no backup key, the key is registered -> check that the key has not changed - if it has, throw an exception + * (v) main key exists, backup key exists, the key is registered -> check in the registry that the backup key has not changed and then re-register main key + */ + protected void validateKeys(KeyMap keys, KeyMap backupKeys) throws InvalidKeystoreException, MissingKeyException + { + if(!validateKeyChanges) + { + return; + } + + writeLock.lock(); + try + { + // check for the existence of a key store first + for(String keyAlias : keysToValidate) + { + if(keys.getKey(keyAlias) == null) + { + if(backupKeys.getKey(keyAlias) == null) + { + if(encryptionKeysRegistry.isKeyRegistered(keyAlias)) + { + // The key is registered and neither key nor backup key exist -> throw + // an exception indicating that the key is missing and the keystore should + // be re-instated. + throw new MissingKeyException(keyAlias, getKeyStoreParameters().getLocation()); + } + else + { + // Neither the key nor the backup key exist, so create the key + createKey(keyAlias); + } + } + } + else + { + if(!encryptionKeysRegistry.isKeyRegistered(keyAlias)) + { + // The key is not registered, so register it + encryptionKeysRegistry.registerKey(keyAlias, keys.getKey(keyAlias)); + } + else if(backupKeys.getKey(keyAlias) == null && encryptionKeysRegistry.checkKey(keyAlias, keys.getKey(keyAlias)) == KEY_STATUS.CHANGED) + { + // A key has been changed, indicating that the keystore has been un-intentionally changed. + // Note: this will halt the application bootstrap. + throw new InvalidKeystoreException("The key with alias " + keyAlias + " has been changed, re-instate the previous keystore"); + } + else if(backupKeys.getKey(keyAlias) != null && encryptionKeysRegistry.isKeyRegistered(keyAlias)) + { + // Both key and backup key exist and the key is registered. + if(encryptionKeysRegistry.checkKey(keyAlias, backupKeys.getKey(keyAlias)) == KEY_STATUS.OK) + { + // The registered key is the backup key so lets re-register the key in the main key store. + // Unregister the existing (now backup) key and re-register the main key. + encryptionKeysRegistry.unregisterKey(keyAlias); + encryptionKeysRegistry.registerKey(keyAlias, keys.getKey(keyAlias)); + } + } + } + } + } + finally + { + writeLock.unlock(); + } + } + + public static class KeyInformation + { + protected String alias; + protected byte[] keyData; + protected String password; + protected String keyAlgorithm; + + public KeyInformation(String alias, byte[] keyData, String password, String keyAlgorithm) + { + super(); + this.alias = alias; + this.keyData = keyData; + this.password = password; + this.keyAlgorithm = keyAlgorithm; + } + + public String getAlias() + { + return alias; + } + + public byte[] getKeyData() + { + return keyData; + } + + public String getPassword() + { + return password; + } + + public String getKeyAlgorithm() + { + return keyAlgorithm; + } + } + + /* + * Caches key meta data information such as password, seed. + * + */ + public static class KeyInfoManager + { + private KeyResourceLoader keyResourceLoader; + private String metadataFileLocation; + private Properties keyProps; + private String keyStorePassword = null; + private Map keyInfo; + + /** + * For testing. + * + * @param passwords + */ + KeyInfoManager(Map passwords, KeyResourceLoader keyResourceLoader) + { + this.keyResourceLoader = keyResourceLoader; + keyInfo = new HashMap(2); + for(Map.Entry password : passwords.entrySet()) + { + keyInfo.put(password.getKey(), new KeyInformation(password.getKey(), null, password.getValue(), null)); + } + } + + KeyInfoManager(String metadataFileLocation, KeyResourceLoader keyResourceLoader) throws IOException, FileNotFoundException + { + this.keyResourceLoader = keyResourceLoader; + this.metadataFileLocation = metadataFileLocation; + keyInfo = new HashMap(2); + loadKeyMetaData(); + } + + public Map getKeyInfo() + { + // TODO defensively copy + return keyInfo; + } + + /** + * Set the map of key meta data (including passwords to access the keystore). + *

+ * Where required, null values must be inserted into the map to indicate the presence + * of a key that is not protected by a password. They entry for {@link #KEY_KEYSTORE_PASSWORD} + * is required if the keystore is password protected. + */ + protected void loadKeyMetaData() throws IOException, FileNotFoundException + { + keyProps = keyResourceLoader.loadKeyMetaData(metadataFileLocation); + if(keyProps != null) + { + String aliases = keyProps.getProperty("aliases"); + if(aliases == null) + { + throw new AlfrescoRuntimeException("Passwords file must contain an aliases key"); + } + + this.keyStorePassword = keyProps.getProperty(KEY_KEYSTORE_PASSWORD); + + StringTokenizer st = new StringTokenizer(aliases, ","); + while(st.hasMoreTokens()) + { + String keyAlias = st.nextToken(); + keyInfo.put(keyAlias, loadKeyInformation(keyAlias)); + } + } + else + { + // TODO + //throw new FileNotFoundException("Cannot find key metadata file " + getKeyMetaDataFileLocation()); + } + } + + public void clear() + { + this.keyStorePassword = null; + if(this.keyProps != null) + { + this.keyProps.clear(); + } + } + + public void removeKeyInformation(String keyAlias) + { + this.keyProps.remove(keyAlias); + } + + protected KeyInformation loadKeyInformation(String keyAlias) + { + String keyPassword = keyProps.getProperty(keyAlias + ".password"); + String keyData = keyProps.getProperty(keyAlias + ".keyData"); + String keyAlgorithm = keyProps.getProperty(keyAlias + ".algorithm"); + + byte[] keyDataBytes = null; + if(keyData != null && !keyData.equals("")) + { + keyDataBytes = Base64.decodeBase64(keyData); + } + KeyInformation keyInfo = new KeyInformation(keyAlias, keyDataBytes, keyPassword, keyAlgorithm); + return keyInfo; + } + + public String getKeyStorePassword() + { + return keyStorePassword; + } + + public void clearKeyStorePassword() + { + this.keyStorePassword = null; + } + + public KeyInformation getKeyInformation(String keyAlias) + { + return keyInfo.get(keyAlias); + } + } +} diff --git a/src/main/java/org/alfresco/encryption/CachedKey.java b/src/main/java/org/alfresco/encryption/CachedKey.java new file mode 100644 index 0000000000..9d24ee0598 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/CachedKey.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.security.Key; + +/** + * + * Represents a loaded, cached encryption key. The key can be null. + * + * @since 4.0 + * + */ +public class CachedKey +{ + public static CachedKey NULL = new CachedKey(null, null); + + private Key key; + private long timestamp; + + CachedKey(Key key, Long timestamp) + { + this.key = key; + this.timestamp = (timestamp != null ? timestamp.longValue() : -1); + } + + public CachedKey(Key key) + { + super(); + this.key = key; + this.timestamp = System.currentTimeMillis(); + } + + public Key getKey() + { + return key; + } + + public long getTimestamp() + { + return timestamp; + } +} diff --git a/src/main/java/org/alfresco/encryption/DecryptingInputStream.java b/src/main/java/org/alfresco/encryption/DecryptingInputStream.java new file mode 100644 index 0000000000..97a0e5bf7f --- /dev/null +++ b/src/main/java/org/alfresco/encryption/DecryptingInputStream.java @@ -0,0 +1,355 @@ +/* + * Copyright 2005-2010 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.encryption; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * An input stream that encrypts data produced by a {@link EncryptingOutputStream}. A lightweight yet secure hybrid + * encryption scheme is used. A random symmetric key is decrypted using the receiver's private key. The supplied data is + * then decrypted using the symmetric key and read on a streaming basis. When the end of the stream is reached or the + * stream is closed, a HMAC checksum of the entire stream contents is validated. + */ +public class DecryptingInputStream extends InputStream +{ + + /** The wrapped stream. */ + private final DataInputStream wrapped; + + /** The input cipher. */ + private final Cipher inputCipher; + + /** The MAC generator. */ + private final Mac mac; + + /** Internal buffer for MAC computation. */ + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024); + + /** A DataOutputStream on top of our interal buffer. */ + private final DataOutputStream dataStr = new DataOutputStream(this.buffer); + + /** The current unencrypted data block. */ + private byte[] currentDataBlock; + + /** The next encrypted data block. (could be the HMAC checksum) */ + private byte[] nextDataBlock; + + /** Have we read to the end of the underlying stream?. */ + private boolean isAtEnd; + + /** Our current position within currentDataBlock. */ + private int currentDataPos; + + /** + * Constructs a DecryptingInputStream using default symmetric encryption parameters. + * + * @param wrapped + * the input stream to decrypt + * @param privKey + * the receiver's private key for decrypting the symmetric key + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws NoSuchAlgorithmException + * the no such algorithm exception + * @throws NoSuchPaddingException + * the no such padding exception + * @throws InvalidKeyException + * the invalid key exception + * @throws IllegalBlockSizeException + * the illegal block size exception + * @throws BadPaddingException + * the bad padding exception + * @throws InvalidAlgorithmParameterException + * the invalid algorithm parameter exception + * @throws NoSuchProviderException + * the no such provider exception + */ + public DecryptingInputStream(final InputStream wrapped, final PrivateKey privKey) throws IOException, + NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, + BadPaddingException, InvalidAlgorithmParameterException, NoSuchProviderException + { + this(wrapped, privKey, "AES", "CBC", "PKCS5PADDING"); + } + + /** + * Constructs a DecryptingInputStream. + * + * @param wrapped + * the input stream to decrypt + * @param privKey + * the receiver's private key for decrypting the symmetric key + * @param algorithm + * encryption algorithm (e.g. "AES") + * @param mode + * encryption mode (e.g. "CBC") + * @param padding + * padding scheme (e.g. "PKCS5PADDING") + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws NoSuchAlgorithmException + * the no such algorithm exception + * @throws NoSuchPaddingException + * the no such padding exception + * @throws InvalidKeyException + * the invalid key exception + * @throws IllegalBlockSizeException + * the illegal block size exception + * @throws BadPaddingException + * the bad padding exception + * @throws InvalidAlgorithmParameterException + * the invalid algorithm parameter exception + * @throws NoSuchProviderException + * the no such provider exception + */ + public DecryptingInputStream(final InputStream wrapped, final PrivateKey privKey, final String algorithm, + final String mode, final String padding) throws IOException, NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException, NoSuchProviderException + { + // Initialise a secure source of randomness + this.wrapped = new DataInputStream(wrapped); + final SecureRandom secRand = SecureRandom.getInstance("SHA1PRNG"); + + // Set up RSA + final Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING"); + rsa.init(Cipher.DECRYPT_MODE, privKey, secRand); + + // Read and decrypt the symmetric key + final SecretKey symKey = new SecretKeySpec(rsa.doFinal(readBlock()), algorithm); + + // Read and decrypt initialisation vector + final byte[] keyIV = rsa.doFinal(readBlock()); + + // Set up cipher for decryption + this.inputCipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding); + this.inputCipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV)); + + // Read and decrypt the MAC key + final SecretKey macKey = new SecretKeySpec(this.inputCipher.doFinal(readBlock()), "HMACSHA1"); + + // Set up HMAC + this.mac = Mac.getInstance("HMACSHA1"); + this.mac.init(macKey); + + // Always read a block ahead so we can intercept the HMAC block + this.nextDataBlock = readBlock(false); + } + + /** + * Reads the next block of data, adding it to the HMAC checksum. Strips the header recording the number of bytes in + * the block. + * + * @return the data block, or null if the end of the stream has been reached + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private byte[] readBlock() throws IOException + { + return readBlock(true); + } + + /** + * Reads the next block of data, optionally adding it to the HMAC checksum. Strips the header recording the number + * of bytes in the block. + * + * @param updateMac + * should the block be added to the HMAC checksum? + * @return the data block, or null if the end of the stream has been reached + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private byte[] readBlock(final boolean updateMac) throws IOException + { + int len; + try + { + len = this.wrapped.readInt(); + } + catch (final EOFException e) + { + return null; + } + final byte[] in = new byte[len]; + this.wrapped.readFully(in); + if (updateMac) + { + macBlock(in); + } + return in; + } + + /** + * Updates the HMAC checksum with the given data block. + * + * @param block + * the block + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private void macBlock(final byte[] block) throws IOException + { + this.dataStr.writeInt(block.length); + this.dataStr.write(block); + // If we don't have the MAC key yet, buffer up until we do + if (this.mac != null) + { + this.dataStr.flush(); + final byte[] bytes = this.buffer.toByteArray(); + this.buffer.reset(); + this.mac.update(bytes); + } + } + + /* + * (non-Javadoc) + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException + { + final byte[] buf = new byte[1]; + int bytesRead; + while ((bytesRead = read(buf)) == 0) + { + ; + } + return bytesRead == -1 ? -1 : buf[0] & 0xFF; + } + + /* + * (non-Javadoc) + * @see java.io.InputStream#read(byte[]) + */ + @Override + public int read(final byte b[]) throws IOException + { + return read(b, 0, b.length); + } + + /* + * (non-Javadoc) + * @see java.io.InputStream#read(byte[], int, int) + */ + @Override + public int read(final byte b[], int off, final int len) throws IOException + { + if (b == null) + { + throw new NullPointerException(); + } + else if (off < 0 || off > b.length || len < 0 || off + len > b.length || off + len < 0) + { + throw new IndexOutOfBoundsException(); + } + else if (len == 0) + { + return 0; + } + + int bytesToRead = len; + OUTER: while (bytesToRead > 0) + { + // Fetch another block if necessary + while (this.currentDataBlock == null || this.currentDataPos >= this.currentDataBlock.length) + { + byte[] newDataBlock; + // We're right at the end of the last block so finish + if (this.isAtEnd) + { + this.currentDataBlock = this.nextDataBlock = null; + break OUTER; + } + // We've already read the last block so validate the MAC code + else if ((newDataBlock = readBlock(false)) == null) + { + if (!MessageDigest.isEqual(this.mac.doFinal(), this.nextDataBlock)) + { + throw new IOException("Invalid HMAC"); + } + // We still have what's left in the cipher to read + try + { + this.currentDataBlock = this.inputCipher.doFinal(); + } + catch (final GeneralSecurityException e) + { + throw new RuntimeException(e); + } + this.isAtEnd = true; + } + // We have an ordinary data block to MAC and decrypt + else + { + macBlock(this.nextDataBlock); + this.currentDataBlock = this.inputCipher.update(this.nextDataBlock); + this.nextDataBlock = newDataBlock; + } + this.currentDataPos = 0; + } + final int bytesRead = Math.min(bytesToRead, this.currentDataBlock.length - this.currentDataPos); + System.arraycopy(this.currentDataBlock, this.currentDataPos, b, off, bytesRead); + bytesToRead -= bytesRead; + off += bytesRead; + this.currentDataPos += bytesRead; + } + return bytesToRead == len ? -1 : len - bytesToRead; + } + + /* + * (non-Javadoc) + * @see java.io.InputStream#available() + */ + @Override + public int available() throws IOException + { + return this.currentDataBlock == null ? 0 : this.currentDataBlock.length - this.currentDataPos; + } + + /* + * (non-Javadoc) + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException + { + // Read right to the end, just to ensure the MAC code is valid! + if (this.nextDataBlock != null) + { + final byte[] skipBuff = new byte[1024]; + while (read(skipBuff) != -1) + { + ; + } + } + this.wrapped.close(); + this.dataStr.close(); + } + +} diff --git a/src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java b/src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java new file mode 100644 index 0000000000..6bceef2a9d --- /dev/null +++ b/src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.AlgorithmParameters; +import java.util.Arrays; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.encryption.MACUtils.MACInput; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.IPUtils; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.Base64; +import org.springframework.util.FileCopyUtils; + +/** + * Various encryption utility methods. + * + * @since 4.0 + */ +public class DefaultEncryptionUtils implements EncryptionUtils +{ + // Logger + protected static Log logger = LogFactory.getLog(Encryptor.class); + + protected static String HEADER_ALGORITHM_PARAMETERS = "XAlfresco-algorithmParameters"; + protected static String HEADER_MAC = "XAlfresco-mac"; + protected static String HEADER_TIMESTAMP = "XAlfresco-timestamp"; + + protected Encryptor encryptor; + protected MACUtils macUtils; + protected long messageTimeout; // ms + protected String remoteIP; + protected String localIP; + + public DefaultEncryptionUtils() + { + try + { + this.localIP = InetAddress.getLocalHost().getHostAddress(); + } + catch(Exception e) + { + throw new AlfrescoRuntimeException("Unable to initialise EncryptionUtils", e); + } + } + + public String getRemoteIP() + { + return remoteIP; + } + + public void setRemoteIP(String remoteIP) + { + try + { + this.remoteIP = IPUtils.getRealIPAddress(remoteIP); + } + catch (UnknownHostException e) + { + throw new AlfrescoRuntimeException("Failed to get server IP address", e); + } + } + + /** + * Get the local registered IP address for authentication purposes + * + * @return String + */ + protected String getLocalIPAddress() + { + return localIP; + } + + public void setMessageTimeout(long messageTimeout) + { + this.messageTimeout = messageTimeout; + } + + public void setEncryptor(Encryptor encryptor) + { + this.encryptor = encryptor; + } + + public void setMacUtils(MACUtils macUtils) + { + this.macUtils = macUtils; + } + + protected void setRequestMac(HttpMethod method, byte[] mac) + { + if(mac == null) + { + throw new AlfrescoRuntimeException("Mac cannot be null"); + } + method.setRequestHeader(HEADER_MAC, Base64.encodeBytes(mac)); + } + + /** + * Set the MAC on the HTTP response + * + * @param response HttpServletResponse + * @param mac byte[] + */ + protected void setMac(HttpServletResponse response, byte[] mac) + { + if(mac == null) + { + throw new AlfrescoRuntimeException("Mac cannot be null"); + } + + response.setHeader(HEADER_MAC, Base64.encodeBytes(mac)); + } + + /** + * Get the MAC (Message Authentication Code) on the HTTP request + * + * @param req HttpServletRequest + * @return the MAC + * @throws IOException + */ + protected byte[] getMac(HttpServletRequest req) throws IOException + { + String header = req.getHeader(HEADER_MAC); + if(header != null) + { + return Base64.decode(header); + } + else + { + return null; + } + } + + /** + * Get the MAC (Message Authentication Code) on the HTTP response + * + * @param res HttpMethod + * @return the MAC + * @throws IOException + */ + protected byte[] getResponseMac(HttpMethod res) throws IOException + { + Header header = res.getResponseHeader(HEADER_MAC); + if(header != null) + { + return Base64.decode(header.getValue()); + } + else + { + return null; + } + } + + /** + * Set the timestamp on the HTTP request + * @param method HttpMethod + * @param timestamp (ms, in UNIX time) + */ + protected void setRequestTimestamp(HttpMethod method, long timestamp) + { + method.setRequestHeader(HEADER_TIMESTAMP, String.valueOf(timestamp)); + } + + /** + * Set the timestamp on the HTTP response + * @param res HttpServletResponse + * @param timestamp (ms, in UNIX time) + */ + protected void setTimestamp(HttpServletResponse res, long timestamp) + { + res.setHeader(HEADER_TIMESTAMP, String.valueOf(timestamp)); + } + + /** + * Get the timestamp on the HTTP response + * + * @param method HttpMethod + * @return timestamp (ms, in UNIX time) + * @throws IOException + */ + protected Long getResponseTimestamp(HttpMethod method) throws IOException + { + Header header = method.getResponseHeader(HEADER_TIMESTAMP); + if(header != null) + { + return Long.valueOf(header.getValue()); + } + else + { + return null; + } + } + + /** + * Get the timestamp on the HTTP request + * + * @param method HttpServletRequest + * @return timestamp (ms, in UNIX time) + * @throws IOException + */ + protected Long getTimestamp(HttpServletRequest method) throws IOException + { + String header = method.getHeader(HEADER_TIMESTAMP); + if(header != null) + { + return Long.valueOf(header); + } + else + { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setRequestAlgorithmParameters(HttpMethod method, AlgorithmParameters params) throws IOException + { + if(params != null) + { + method.setRequestHeader(HEADER_ALGORITHM_PARAMETERS, Base64.encodeBytes(params.getEncoded())); + } + } + + /** + * Set the algorithm parameters header on the HTTP response + * + * @param response HttpServletResponse + * @param params AlgorithmParameters + * @throws IOException + */ + protected void setAlgorithmParameters(HttpServletResponse response, AlgorithmParameters params) throws IOException + { + if(params != null) + { + response.setHeader(HEADER_ALGORITHM_PARAMETERS, Base64.encodeBytes(params.getEncoded())); + } + } + + /** + * Decode cipher algorithm parameters from the HTTP method + * + * @param method HttpMethod + * @return decoded algorithm parameters + * @throws IOException + */ + protected AlgorithmParameters decodeAlgorithmParameters(HttpMethod method) throws IOException + { + Header header = method.getResponseHeader(HEADER_ALGORITHM_PARAMETERS); + if(header != null) + { + byte[] algorithmParams = Base64.decode(header.getValue()); + AlgorithmParameters algorithmParameters = encryptor.decodeAlgorithmParameters(algorithmParams); + return algorithmParameters; + } + else + { + return null; + } + } + + /** + * Decode cipher algorithm parameters from the HTTP method + * + * @param req + * @return decoded algorithm parameters + * @throws IOException + */ + protected AlgorithmParameters decodeAlgorithmParameters(HttpServletRequest req) throws IOException + { + String header = req.getHeader(HEADER_ALGORITHM_PARAMETERS); + if(header != null) + { + byte[] algorithmParams = Base64.decode(header); + AlgorithmParameters algorithmParameters = encryptor.decodeAlgorithmParameters(algorithmParams); + return algorithmParameters; + } + else + { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] decryptResponseBody(HttpMethod method) throws IOException + { + // TODO fileoutputstream if content is especially large? + InputStream body = method.getResponseBodyAsStream(); + if(body != null) + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + FileCopyUtils.copy(body, out); + + AlgorithmParameters params = decodeAlgorithmParameters(method); + if(params != null) + { + byte[] decrypted = encryptor.decrypt(KeyProvider.ALIAS_SOLR, params, out.toByteArray()); + return decrypted; + } + else + { + throw new AlfrescoRuntimeException("Unable to decrypt response body, missing encryption algorithm parameters"); + } + } + else + { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] decryptBody(HttpServletRequest req) throws IOException + { + if(req.getMethod().equals("POST")) + { + InputStream bodyStream = req.getInputStream(); + if(bodyStream != null) + { + // expect algorithParameters header + AlgorithmParameters p = decodeAlgorithmParameters(req); + + // decrypt the body + InputStream in = encryptor.decrypt(KeyProvider.ALIAS_SOLR, p, bodyStream); + return IOUtils.toByteArray(in); + } + else + { + return null; + } + } + else + { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean authenticateResponse(HttpMethod method, String remoteIP, byte[] decryptedBody) + { + try + { + byte[] expectedMAC = getResponseMac(method); + Long timestamp = getResponseTimestamp(method); + if(timestamp == null) + { + return false; + } + remoteIP = IPUtils.getRealIPAddress(remoteIP); + return authenticate(expectedMAC, new MACInput(decryptedBody, timestamp.longValue(), remoteIP)); + } + catch(Exception e) + { + throw new RuntimeException("Unable to authenticate HTTP response", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean authenticate(HttpServletRequest req, byte[] decryptedBody) + { + try + { + byte[] expectedMAC = getMac(req); + Long timestamp = getTimestamp(req); + if(timestamp == null) + { + return false; + } + String ipAddress = IPUtils.getRealIPAddress(req.getRemoteAddr()); + return authenticate(expectedMAC, new MACInput(decryptedBody, timestamp.longValue(), ipAddress)); + } + catch(Exception e) + { + throw new AlfrescoRuntimeException("Unable to authenticate HTTP request", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setRequestAuthentication(HttpMethod method, byte[] message) throws IOException + { + long requestTimestamp = System.currentTimeMillis(); + + // add MAC header + byte[] mac = macUtils.generateMAC(KeyProvider.ALIAS_SOLR, new MACInput(message, requestTimestamp, getLocalIPAddress())); + + if(logger.isDebugEnabled()) + { + logger.debug("Setting MAC " + Arrays.toString(mac) + " on HTTP request " + method.getPath()); + logger.debug("Setting timestamp " + requestTimestamp + " on HTTP request " + method.getPath()); + } + + setRequestMac(method, mac); + + // prevent replays + setRequestTimestamp(method, requestTimestamp); + } + + /** + * {@inheritDoc} + */ + @Override + public void setResponseAuthentication(HttpServletRequest httpRequest, HttpServletResponse httpResponse, + byte[] responseBody, AlgorithmParameters params) throws IOException + { + long responseTimestamp = System.currentTimeMillis(); + byte[] mac = macUtils.generateMAC(KeyProvider.ALIAS_SOLR, + new MACInput(responseBody, responseTimestamp, getLocalIPAddress())); + + if(logger.isDebugEnabled()) + { + logger.debug("Setting MAC " + Arrays.toString(mac) + " on HTTP response to request " + httpRequest.getRequestURI()); + logger.debug("Setting timestamp " + responseTimestamp + " on HTTP response to request " + httpRequest.getRequestURI()); + } + + setAlgorithmParameters(httpResponse, params); + setMac(httpResponse, mac); + + // prevent replays + setTimestamp(httpResponse, responseTimestamp); + } + + protected boolean authenticate(byte[] expectedMAC, MACInput macInput) + { + // check the MAC and, if valid, check that the timestamp is under the threshold and that the remote IP is + // the expected IP + boolean authorized = macUtils.validateMAC(KeyProvider.ALIAS_SOLR, expectedMAC, macInput) && + validateTimestamp(macInput.getTimestamp()); + return authorized; + } + + protected boolean validateTimestamp(long timestamp) + { + long currentTime = System.currentTimeMillis(); + return((currentTime - timestamp) < messageTimeout); + } + +} diff --git a/src/main/java/org/alfresco/encryption/DefaultEncryptor.java b/src/main/java/org/alfresco/encryption/DefaultEncryptor.java new file mode 100644 index 0000000000..630fa3d56b --- /dev/null +++ b/src/main/java/org/alfresco/encryption/DefaultEncryptor.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.PropertyCheck; + +/** + * @author Derek Hulley + * @since 4.0 + */ +public class DefaultEncryptor extends AbstractEncryptor +{ + private boolean cacheCiphers = true; + private final ThreadLocal> threadCipher; + + /** + * Default constructor for IOC + */ + public DefaultEncryptor() + { + threadCipher = new ThreadLocal>(); + } + + /** + * Convenience constructor for tests + */ + /* package */ DefaultEncryptor(KeyProvider keyProvider, String cipherAlgorithm, String cipherProvider) + { + this(); + setKeyProvider(keyProvider); + setCipherAlgorithm(cipherAlgorithm); + setCipherProvider(cipherProvider); + } + + public void init() + { + super.init(); + PropertyCheck.mandatory(this, "cipherAlgorithm", cipherAlgorithm); + } + + public void setCacheCiphers(boolean cacheCiphers) + { + this.cacheCiphers = cacheCiphers; + } + + protected Cipher createCipher(int mode, String algorithm, String provider, Key key, AlgorithmParameters params) + throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException + { + Cipher cipher = null; + + if (cipherProvider == null) + { + cipher = Cipher.getInstance(algorithm); + } + else + { + cipher = Cipher.getInstance(algorithm, provider); + } + cipher.init(mode, key, params); + + return cipher; + } + + protected Cipher getCachedCipher(String keyAlias, int mode, AlgorithmParameters params, Key key) + throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidAlgorithmParameterException + { + CachedCipher cipherInfo = null; + Cipher cipher = null; + + Map ciphers = threadCipher.get(); + if(ciphers == null) + { + ciphers = new HashMap(5); + threadCipher.set(ciphers); + } + cipherInfo = ciphers.get(new CipherKey(keyAlias, mode)); + if(cipherInfo == null) + { + cipher = createCipher(mode, cipherAlgorithm, cipherProvider, key, params); + ciphers.put(new CipherKey(keyAlias, mode), new CachedCipher(cipher, key)); + + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Cipher constructed: alias=" + keyAlias + "; mode=" + mode + ": " + cipher); + } + } + else + { + // the key has changed, re-construct the cipher + if(cipherInfo.getKey() != key) + { + // key has changed, rendering the cached cipher out of date. Re-create the cipher with + // the new key. + cipher = createCipher(mode, cipherAlgorithm, cipherProvider, key, params); + ciphers.put(new CipherKey(keyAlias, mode), new CachedCipher(cipher, key)); + } + else + { + cipher = cipherInfo.getCipher(); + } + } + + return cipher; + } + + @Override + public Cipher getCipher(String keyAlias, AlgorithmParameters params, int mode) + { + Cipher cipher = null; + + // Get the encryption key + Key key = keyProvider.getKey(keyAlias); + if(key == null) + { + // No encryption possible + return null; + } + + try + { + if(cacheCiphers) + { + cipher = getCachedCipher(keyAlias, mode, params, key); + } + else + { + cipher = createCipher(mode, cipherAlgorithm, cipherProvider, key, params); + } + } + catch (Exception e) + { + throw new AlfrescoRuntimeException( + "Failed to construct cipher: alias=" + keyAlias + "; mode=" + mode, + e); + } + + return cipher; + } + + public boolean keyAvailable(String keyAlias) + { + return keyProvider.getKey(keyAlias) != null; + } + + private static class CipherKey + { + private String keyAlias; + private int mode; + + public CipherKey(String keyAlias, int mode) + { + super(); + this.keyAlias = keyAlias; + this.mode = mode; + } + + public String getKeyAlias() + { + return keyAlias; + } + + public int getMode() + { + return mode; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + + ((keyAlias == null) ? 0 : keyAlias.hashCode()); + result = prime * result + mode; + return result; + } + + @Override + public boolean equals(Object obj) + { + if(this == obj) + { + return true; + } + + if(!(obj instanceof CipherKey)) + { + return false; + } + + CipherKey other = (CipherKey)obj; + if(keyAlias == null) + { + if (other.keyAlias != null) + { + return false; + } + } + else if(!keyAlias.equals(other.keyAlias)) + { + return false; + } + + if(mode != other.mode) + { + return false; + } + + return true; + } + } + + /* + * Stores a cipher and the key used to construct it. + */ + private static class CachedCipher + { + private Key key; + private Cipher cipher; + + public CachedCipher(Cipher cipher, Key key) + { + super(); + this.cipher = cipher; + this.key = key; + } + + public Cipher getCipher() + { + return cipher; + } + + public Key getKey() + { + return key; + } + } +} diff --git a/src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java b/src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java new file mode 100644 index 0000000000..7bf33ca8b4 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.InputStream; +import java.io.Serializable; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.Pair; + +/** + * The fallback encryptor provides a fallback mechanism for decryption, first using the default + * encryption keys and, if they fail (perhaps because they have been changed), falling back + * to a backup set of keys. + * + * Note that encryption will be performed only using the default encryption keys. + * + * @since 4.0 + */ +public class DefaultFallbackEncryptor implements FallbackEncryptor +{ + private Encryptor fallback; + private Encryptor main; + + public DefaultFallbackEncryptor() + { + } + + public DefaultFallbackEncryptor(Encryptor main, Encryptor fallback) + { + this(); + this.main = main; + this.fallback = fallback; + } + + public void setFallback(Encryptor fallback) + { + this.fallback = fallback; + } + + public void setMain(Encryptor main) + { + this.main = main; + } + + /** + * {@inheritDoc} + */ + @Override + public Pair encrypt(String keyAlias, + AlgorithmParameters params, byte[] input) + { + // Note: encrypt supported only for main encryptor + Pair ret = main.encrypt(keyAlias, params, input); + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] decrypt(String keyAlias, AlgorithmParameters params, + byte[] input) + { + byte[] ret; + + // for decryption, try the main encryptor. If that fails (possibly as a result of the keys being updated), + // fall back to fallback encryptor. + try + { + ret = main.decrypt(keyAlias, params, input); + } + catch(Throwable e) + { + ret = fallback.decrypt(keyAlias, params, input); + } + + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public InputStream decrypt(String keyAlias, AlgorithmParameters params, + InputStream in) + { + InputStream ret; + + // for decryption, try the main encryptor. If that fails (possibly as a result of the keys being updated), + // fall back to fallback encryptor. + try + { + ret = main.decrypt(keyAlias, params, in); + } + catch(Throwable e) + { + ret = fallback.decrypt(keyAlias, params, in); + } + + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public Pair encryptObject(String keyAlias, + AlgorithmParameters params, Object input) + { + // Note: encrypt supported only for main encryptor + Pair ret = main.encryptObject(keyAlias, params, input); + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public Object decryptObject(String keyAlias, AlgorithmParameters params, + byte[] input) + { + Object ret; + + // for decryption, try the main encryptor. If that fails (possibly as a result of the keys being updated), + // fall back to fallback encryptor. + try + { + ret = main.decryptObject(keyAlias, params, input); + } + catch(Throwable e) + { + ret = fallback.decryptObject(keyAlias, params, input); + } + + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public Serializable sealObject(String keyAlias, AlgorithmParameters params, + Serializable input) + { + // Note: encrypt supported only for main encryptor + Serializable ret = main.sealObject(keyAlias, params, input); + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public Serializable unsealObject(String keyAlias, Serializable input) + throws InvalidKeyException + { + Serializable ret; + + // for decryption, try the main encryptor. If that fails (possibly as a result of the keys being updated), + // fall back to fallback encryptor. + try + { + ret = main.unsealObject(keyAlias, input); + } + catch(Throwable e) + { + ret = fallback.unsealObject(keyAlias, input); + } + + return ret; + + } + + /** + * {@inheritDoc} + */ + @Override + public AlgorithmParameters decodeAlgorithmParameters(byte[] encoded) + { + AlgorithmParameters ret; + + try + { + ret = main.decodeAlgorithmParameters(encoded); + } + catch(AlfrescoRuntimeException e) + { + ret = fallback.decodeAlgorithmParameters(encoded); + } + + return ret; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean keyAvailable(String keyAlias) + { + return main.keyAvailable(keyAlias); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean backupKeyAvailable(String keyAlias) + { + return fallback.keyAvailable(keyAlias); + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/encryption/EncryptingOutputStream.java b/src/main/java/org/alfresco/encryption/EncryptingOutputStream.java new file mode 100644 index 0000000000..7096da7b18 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/EncryptingOutputStream.java @@ -0,0 +1,252 @@ +/* + * Copyright 2005-2010 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.encryption; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.SecureRandom; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +/** + * An output stream that encrypts data to another output stream. A lightweight yet secure hybrid encryption scheme is + * used. A random symmetric key is generated and encrypted using the receiver's public key. The supplied data is then + * encrypted using the symmetric key and sent to the underlying stream on a streaming basis. An HMAC checksum is also + * computed on an ongoing basis and appended to the output when the stream is closed. This class can be used in + * conjunction with {@link DecryptingInputStream} to transport data securely. + */ +public class EncryptingOutputStream extends OutputStream +{ + /** The wrapped stream. */ + private final OutputStream wrapped; + + /** The output cipher. */ + private final Cipher outputCipher; + + /** The MAC generator. */ + private final Mac mac; + + /** Internal buffer for MAC computation. */ + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024); + + /** A DataOutputStream on top of our interal buffer. */ + private final DataOutputStream dataStr = new DataOutputStream(this.buffer); + + /** + * Constructs an EncryptingOutputStream using default symmetric encryption parameters. + * + * @param wrapped + * outputstream to store the encrypted data + * @param receiverKey + * the receiver's public key for encrypting the symmetric key + * @param rand + * a secure source of randomness + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws NoSuchAlgorithmException + * the no such algorithm exception + * @throws NoSuchPaddingException + * the no such padding exception + * @throws InvalidKeyException + * the invalid key exception + * @throws BadPaddingException + * the bad padding exception + * @throws IllegalBlockSizeException + * the illegal block size exception + */ + public EncryptingOutputStream(final OutputStream wrapped, final PublicKey receiverKey, final SecureRandom rand) + throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + IllegalBlockSizeException, BadPaddingException + { + this(wrapped, receiverKey, "AES", rand, 128, "CBC", "PKCS5PADDING"); + } + + /** + * Constructs an EncryptingOutputStream. + * + * @param wrapped + * outputstream to store the encrypted data + * @param receiverKey + * the receiver's public key for encrypting the symmetric key + * @param algorithm + * symmetric encryption algorithm (e.g. "AES") + * @param rand + * a secure source of randomness + * @param strength + * the key size in bits (e.g. 128) + * @param mode + * encryption mode (e.g. "CBC") + * @param padding + * padding scheme (e.g. "PKCS5PADDING") + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws NoSuchAlgorithmException + * the no such algorithm exception + * @throws NoSuchPaddingException + * the no such padding exception + * @throws InvalidKeyException + * the invalid key exception + * @throws BadPaddingException + * the bad padding exception + * @throws IllegalBlockSizeException + * the illegal block size exception + */ + public EncryptingOutputStream(final OutputStream wrapped, final PublicKey receiverKey, final String algorithm, + final SecureRandom rand, final int strength, final String mode, final String padding) throws IOException, + NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, + BadPaddingException + { + // Initialise + this.wrapped = wrapped; + + // Generate a random symmetric key + final KeyGenerator keyGen = KeyGenerator.getInstance(algorithm); + keyGen.init(strength, rand); + final Key symKey = keyGen.generateKey(); + + // Instantiate Symmetric cipher for encryption. + this.outputCipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding); + this.outputCipher.init(Cipher.ENCRYPT_MODE, symKey, rand); + + // Set up HMAC + this.mac = Mac.getInstance("HMACSHA1"); + final byte[] macKeyBytes = new byte[20]; + rand.nextBytes(macKeyBytes); + final Key macKey = new SecretKeySpec(macKeyBytes, "HMACSHA1"); + this.mac.init(macKey); + + // Set up RSA to encrypt symmetric key + final Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING"); + rsa.init(Cipher.ENCRYPT_MODE, receiverKey, rand); + + // Write the header + + // Write out an RSA-encrypted block for the key of the cipher. + writeBlock(rsa.doFinal(symKey.getEncoded())); + + // Write out RSA-encrypted Initialisation Vector block + writeBlock(rsa.doFinal(this.outputCipher.getIV())); + + // Write out key for HMAC. + writeBlock(this.outputCipher.doFinal(macKey.getEncoded())); + } + + /* + * (non-Javadoc) + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(final int b) throws IOException + { + write(new byte[] + { + (byte) b + }, 0, 1); + } + + /* + * (non-Javadoc) + * @see java.io.OutputStream#write(byte[]) + */ + @Override + public void write(final byte b[]) throws IOException + { + write(b, 0, b.length); + } + + /* + * (non-Javadoc) + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public void write(final byte b[], final int off, final int len) throws IOException + { + if (b == null) + { + throw new NullPointerException(); + } + else if (off < 0 || off > b.length || len < 0 || off + len > b.length || off + len < 0) + { + throw new IndexOutOfBoundsException(); + } + else if (len == 0) + { + return; + } + final byte[] out = this.outputCipher.update(b, off, len); // Encrypt data. + if (out != null && out.length > 0) + { + writeBlock(out); + } + } + + /** + * Writes a block of data, preceded by its length, and adds it to the HMAC checksum. + * + * @param out + * the data to be written. + * @throws IOException + * Signals that an I/O exception has occurred. + */ + private void writeBlock(final byte[] out) throws IOException + { + this.dataStr.writeInt(out.length); // Write length. + this.dataStr.write(out); // Write encrypted data. + this.dataStr.flush(); + final byte[] block = this.buffer.toByteArray(); + this.buffer.reset(); + this.mac.update(block); + this.wrapped.write(block); + } + + /* + * (non-Javadoc) + * @see java.io.OutputStream#flush() + */ + @Override + public void flush() throws IOException + { + this.wrapped.flush(); + } + + /* + * (non-Javadoc) + * @see java.io.OutputStream#close() + */ + @Override + public void close() throws IOException + { + try + { + // Write the last block + writeBlock(this.outputCipher.doFinal()); + } + catch (final GeneralSecurityException e) + { + throw new RuntimeException(e); + } + // Write the MAC code + writeBlock(this.mac.doFinal()); + this.wrapped.close(); + this.dataStr.close(); + } + +} diff --git a/src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java b/src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java new file mode 100644 index 0000000000..bc58421067 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.security.Key; +import java.util.List; +import java.util.Set; + +/** + * Stores registered encryption keys. + * + * @since 4.0 + * + */ +public interface EncryptionKeysRegistry +{ + public static enum KEY_STATUS + { + OK, CHANGED, MISSING; + }; + + /** + * Is the key with alias 'keyAlias' registered? + * @param keyAlias String + * @return boolean + */ + public boolean isKeyRegistered(String keyAlias); + + /** + * Register the key. + * + * @param keyAlias String + * @param key Key + */ + public void registerKey(String keyAlias, Key key); + + /** + * Unregister the key. + * + * @param keyAlias String + */ + public void unregisterKey(String keyAlias); + + /** + * Check the validity of the key against the registry. + * + * @param keyAlias String + * @param key Key + * @return KEY_STATUS + */ + public KEY_STATUS checkKey(String keyAlias, Key key); + + /** + * Remove the set of keys from the registry. + * + * @param keys Set + */ + public void removeRegisteredKeys(Set keys); + + /** + * Return those keys in the set that have been registered. + * + * @param keys Set + * @return List + */ + public List getRegisteredKeys(Set keys); +} diff --git a/src/main/java/org/alfresco/encryption/EncryptionUtils.java b/src/main/java/org/alfresco/encryption/EncryptionUtils.java new file mode 100644 index 0000000000..d8211b3c45 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/EncryptionUtils.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.IOException; +import java.security.AlgorithmParameters; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.httpclient.HttpMethod; + +/** + * Various encryption utility methods. + * + * @since 4.0 + */ +public interface EncryptionUtils +{ + /** + * Decrypt the response body of the http method + * + * @param method + * @return decrypted response body + * @throws IOException + */ + public byte[] decryptResponseBody(HttpMethod method) throws IOException; + + /** + * Decrypt the body of the http request + * + * @param req + * @return decrypted response body + * @throws IOException + */ + public byte[] decryptBody(HttpServletRequest req) throws IOException; + + /** + * Authenticate the http method response: validate the MAC, check that the remote IP is + * as expected and that the timestamp is recent. + * + * @param method + * @param remoteIP + * @param decryptedBody + * @return true if the method reponse is authentic, false otherwise + */ + public boolean authenticateResponse(HttpMethod method, String remoteIP, byte[] decryptedBody); + + /** + * Authenticate the http request: validate the MAC, check that the remote IP is + * as expected and that the timestamp is recent. + * + * @param req + * @param decryptedBody + * @return true if the method request is authentic, false otherwise + */ + public boolean authenticate(HttpServletRequest req, byte[] decryptedBody); + + /** + * Encrypt the http method request body + * + * @param method + * @param message + * @throws IOException + */ + public void setRequestAuthentication(HttpMethod method, byte[] message) throws IOException; + + /** + * Sets authentication headers on the HTTP response. + * + * @param httpRequest + * @param httpResponse + * @param responseBody + * @param params + * @throws IOException + */ + public void setResponseAuthentication(HttpServletRequest httpRequest, HttpServletResponse httpResponse, + byte[] responseBody, AlgorithmParameters params) throws IOException; + + /** + * Set the algorithm parameters header on the method request + * + * @param method + * @param params + * @throws IOException + */ + public void setRequestAlgorithmParameters(HttpMethod method, AlgorithmParameters params) throws IOException; +} diff --git a/src/main/java/org/alfresco/encryption/Encryptor.java b/src/main/java/org/alfresco/encryption/Encryptor.java new file mode 100644 index 0000000000..dc58db43e3 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/Encryptor.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.InputStream; +import java.io.Serializable; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; + +import org.alfresco.util.Pair; + +/** + * Interface providing methods to encrypt and decrypt data. + * + * @since 4.0 + */ +public interface Encryptor +{ + /** + * Encrypt some bytes + * + * @param keyAlias the encryption key alias + * @param input the data to encrypt + * @return the encrypted data and parameters used + */ + Pair encrypt(String keyAlias, AlgorithmParameters params, byte[] input); + + /** + * Decrypt some bytes + * + * @param keyAlias the encryption key alias + * @param input the data to decrypt + * @return the unencrypted data + */ + byte[] decrypt(String keyAlias, AlgorithmParameters params, byte[] input); + + /** + * Decrypt an input stream + * + * @param keyAlias the encryption key alias + * @param in the data to decrypt + * @return the unencrypted data + */ + InputStream decrypt(String keyAlias, AlgorithmParameters params, InputStream in); + + /** + * Encrypt an object + * + * @param keyAlias the encryption key alias + * @param input the object to write to bytes + * @return the encrypted data and parameters used + */ + Pair encryptObject(String keyAlias, AlgorithmParameters params, Object input); + + /** + * Decrypt data as an object + * + * @param keyAlias the encryption key alias + * @param input the data to decrypt + * @return the unencrypted data deserialized + */ + Object decryptObject(String keyAlias, AlgorithmParameters params, byte[] input); + + /** + * Convenience method to seal on object up cryptographically. + *

+ * Note that the original object may be returned directly if there is no key associated with + * the alias. + * + * @param keyAlias the encryption key alias + * @param input the object to encrypt and seal + * @return the sealed object that can be decrypted with the original key + */ + Serializable sealObject(String keyAlias, AlgorithmParameters params, Serializable input); + + /** + * Convenience method to unseal on object sealed up cryptographically. + *

+ * Note that the algorithm parameters not provided on the assumption that a symmetric key + * algorithm is in use - only the key is required for unsealing. + *

+ * Note that the original object may be returned directly if there is no key associated with + * the alias or if the input object is not a SealedObject. + * + * @param keyAlias the encryption key alias + * @param input the object to decrypt and unseal + * @return the original unsealed object that was encrypted with the original key + * @throws IllegalStateException if the key alias is not valid and the input is a + * SealedObject + */ + Serializable unsealObject(String keyAlias, Serializable input) throws InvalidKeyException; + + /** + * Decodes encoded cipher algorithm parameters + * + * @param encoded the encoded cipher algorithm parameters + * @return the decoded cipher algorithmParameters + */ + AlgorithmParameters decodeAlgorithmParameters(byte[] encoded); + + boolean keyAvailable(String keyAlias); +} diff --git a/src/main/java/org/alfresco/encryption/FallbackEncryptor.java b/src/main/java/org/alfresco/encryption/FallbackEncryptor.java new file mode 100644 index 0000000000..a5428fcadd --- /dev/null +++ b/src/main/java/org/alfresco/encryption/FallbackEncryptor.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +/** + * A fallback encryptor provides a fallback mechanism for decryption, first using the default + * encryption keys and, if they fail (perhaps because they have been changed), falling back + * to a backup set of keys. + * + * Note that encryption will be performed only using the default encryption keys. + * + * @since 4.0 + */ +public interface FallbackEncryptor extends Encryptor +{ + /** + * Is the backup key available in order to fall back to? + * + * @return boolean + */ + boolean backupKeyAvailable(String keyAlias); +} diff --git a/src/main/java/org/alfresco/encryption/GenerateSecretKey.java b/src/main/java/org/alfresco/encryption/GenerateSecretKey.java new file mode 100644 index 0000000000..8e10773505 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/GenerateSecretKey.java @@ -0,0 +1,47 @@ +package org.alfresco.encryption; + +import java.security.SecureRandom; + +import javax.crypto.spec.DESedeKeySpec; + +import org.apache.commons.codec.binary.Base64; + +/** + * + * Generate a secret key for use by the repository. + * + * @since 4.0 + * + */ +public class GenerateSecretKey +{ + public byte[] generateKeyData() + { + try + { + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + random.setSeed(System.currentTimeMillis()); + byte bytes[] = new byte[DESedeKeySpec.DES_EDE_KEY_LEN]; + random.nextBytes(bytes); + return bytes; + } + catch(Exception e) + { + throw new RuntimeException("Unable to generate secret key", e); + } + } + + public static void main(String args[]) + { + try + { + GenerateSecretKey gen = new GenerateSecretKey(); + byte[] bytes = gen.generateKeyData(); + System.out.print(Base64.encodeBase64String(bytes)); + } + catch(Throwable e) + { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/alfresco/encryption/InvalidKeystoreException.java b/src/main/java/org/alfresco/encryption/InvalidKeystoreException.java new file mode 100644 index 0000000000..4048f9c0bc --- /dev/null +++ b/src/main/java/org/alfresco/encryption/InvalidKeystoreException.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +/** + * + * @since 4.0 + * + */ +public class InvalidKeystoreException extends Exception +{ + private static final long serialVersionUID = -1324791685965572313L; + + public InvalidKeystoreException(String message) + { + super(message); + } +} diff --git a/src/main/java/org/alfresco/encryption/KeyMap.java b/src/main/java/org/alfresco/encryption/KeyMap.java new file mode 100644 index 0000000000..74e9e2cec7 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/KeyMap.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.security.Key; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * A simple map of key aliases to keys. Each key has an associated timestamp indicating + * when it was last loaded from the keystore on disk. + * + * @since 4.0 + * + */ +public class KeyMap +{ + private Map keys; + + public KeyMap() + { + this.keys = new HashMap(5); + } + + public KeyMap(Map keys) + { + super(); + this.keys = keys; + } + + public int numKeys() + { + return keys.size(); + } + + public Set getKeyAliases() + { + return keys.keySet(); + } + + // always returns an instance; if null will return a CachedKey.NULL + public CachedKey getCachedKey(String keyAlias) + { + CachedKey cachedKey = keys.get(keyAlias); + return (cachedKey != null ? cachedKey : CachedKey.NULL); + } + + public Key getKey(String keyAlias) + { + return getCachedKey(keyAlias).getKey(); + } + + public void setKey(String keyAlias, Key key) + { + keys.put(keyAlias, new CachedKey(key)); + } +} diff --git a/src/main/java/org/alfresco/encryption/KeyProvider.java b/src/main/java/org/alfresco/encryption/KeyProvider.java new file mode 100644 index 0000000000..a24a398953 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/KeyProvider.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.security.Key; + +/** + * A key provider returns the secret keys for different use cases. + * + * @since 4.0 + */ +public interface KeyProvider +{ + // TODO: Allow the aliases to be configured i.e. include an alias mapper + /** + * Constant representing the keystore alias for keys to encrypt/decrypt node metadata + */ + public static final String ALIAS_METADATA = "metadata"; + + /** + * Constant representing the keystore alias for keys to encrypt/decrypt SOLR transfer data + */ + public static final String ALIAS_SOLR = "solr"; + + /** + * Get an encryption key if available. + * + * @param keyAlias the key alias + * @return the encryption key and a timestamp of when it was last changed + */ + public Key getKey(String keyAlias); +} diff --git a/src/main/java/org/alfresco/encryption/KeyResourceLoader.java b/src/main/java/org/alfresco/encryption/KeyResourceLoader.java new file mode 100644 index 0000000000..f32325ed80 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/KeyResourceLoader.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * Manages key resources (key store and key store passwords) + * + * @since 4.0 + * + */ +public interface KeyResourceLoader +{ + /** + * Loads and returns an InputStream of the key store at the configured location. + * If the file cannot be found this method returns null. + * + * @return InputStream + * @throws FileNotFoundException + */ + public InputStream getKeyStore(String keyStoreLocation) throws FileNotFoundException; + + /** + * Loads key metadata from the configured passwords file location. + * + * Note that the passwords are not cached locally. + * If the file cannot be found this method returns null. + * + * @return Properties + * @throws IOException + */ + public Properties loadKeyMetaData(String keyMetaDataFileLocation) throws IOException, FileNotFoundException; +} diff --git a/src/main/java/org/alfresco/encryption/KeyStoreParameters.java b/src/main/java/org/alfresco/encryption/KeyStoreParameters.java new file mode 100644 index 0000000000..5b8ebe4504 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/KeyStoreParameters.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import org.alfresco.util.PropertyCheck; + +/** + * Stores Java keystore initialisation parameters. + * + * @since 4.0 + * + */ +public class KeyStoreParameters +{ + private String name; + private String type; + private String provider; + private String keyMetaDataFileLocation; + private String location; + + public KeyStoreParameters() + { + } + + public KeyStoreParameters(String name, String type, String keyStoreProvider, + String keyMetaDataFileLocation, String location) + { + super(); + this.name = name; + this.type = type; + this.provider = keyStoreProvider; + this.keyMetaDataFileLocation = keyMetaDataFileLocation; + this.location = location; + } + + public void init() + { + if (!PropertyCheck.isValidPropertyString(getLocation())) + { + setLocation(null); + } + if (!PropertyCheck.isValidPropertyString(getProvider())) + { + setProvider(null); + } + if (!PropertyCheck.isValidPropertyString(getType())) + { + setType(null); + } + if (!PropertyCheck.isValidPropertyString(getKeyMetaDataFileLocation())) + { + setKeyMetaDataFileLocation(null); + } + } + + public String getName() + { + return name; + } + + public String getType() + { + return type; + } + + public String getProvider() + { + return provider; + } + + public String getKeyMetaDataFileLocation() + { + return keyMetaDataFileLocation; + } + + public String getLocation() + { + return location; + } + + public void setName(String name) + { + this.name = name; + } + + public void setType(String type) + { + this.type = type; + } + + public void setProvider(String provider) + { + this.provider = provider; + } + + public void setKeyMetaDataFileLocation(String keyMetaDataFileLocation) + { + this.keyMetaDataFileLocation = keyMetaDataFileLocation; + } + + public void setLocation(String location) + { + this.location = location; + } +} diff --git a/src/main/java/org/alfresco/encryption/KeysReport.java b/src/main/java/org/alfresco/encryption/KeysReport.java new file mode 100644 index 0000000000..49f10fce81 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/KeysReport.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.util.List; + +/** + * A report on which keys have changed and which keys have not changed. + * + * @since 4.0 + * + */ +public class KeysReport +{ + private List keysChanged; + private List keysUnchanged; + + public KeysReport(List keysChanged, List keysUnchanged) + { + super(); + this.keysChanged = keysChanged; + this.keysUnchanged = keysUnchanged; + } + + public List getKeysChanged() + { + return keysChanged; + } + + public List getKeysUnchanged() + { + return keysUnchanged; + } +} diff --git a/src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java b/src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java new file mode 100644 index 0000000000..dcc2a5c960 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.security.Key; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * Provides system-wide secret keys for symmetric database encryption from a key store + * in the filesystem. Just wraps a key store. + * + * @author Derek Hulley + * @since 4.0 + */ +public class KeystoreKeyProvider extends AbstractKeyProvider +{ + private static final Log logger = LogFactory.getLog(KeystoreKeyProvider.class); + + private AlfrescoKeyStore keyStore; + private boolean useBackupKeys = false; + + /** + * Constructs the provider with required defaults + */ + public KeystoreKeyProvider() + { + } + + public KeystoreKeyProvider(KeyStoreParameters keyStoreParameters, KeyResourceLoader keyResourceLoader) + { + this(); + this.keyStore = new AlfrescoKeyStoreImpl(keyStoreParameters, keyResourceLoader); + init(); + } + + public void setUseBackupKeys(boolean useBackupKeys) + { + this.useBackupKeys = useBackupKeys; + } + + /** + * + * @param keyStore + */ + public KeystoreKeyProvider(AlfrescoKeyStore keyStore) + { + this(); + this.keyStore = keyStore; + init(); + } + + public void setKeyStore(AlfrescoKeyStore keyStore) + { + this.keyStore = keyStore; + } + + public void init() + { + } + + /** + * {@inheritDoc} + */ + @Override + public Key getKey(String keyAlias) + { + if(useBackupKeys) + { + return keyStore.getBackupKey(keyAlias); + } + else + { + return keyStore.getKey(keyAlias); + } + } +} diff --git a/src/main/java/org/alfresco/encryption/MACUtils.java b/src/main/java/org/alfresco/encryption/MACUtils.java new file mode 100644 index 0000000000..e7f7eaad6b --- /dev/null +++ b/src/main/java/org/alfresco/encryption/MACUtils.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.Key; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.crypto.Mac; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Provides support for generating and checking MACs (Message Authentication Codes) using Alfresco's + * secret keys. + * + * @since 4.0 + * + */ +public class MACUtils +{ + private static Log logger = LogFactory.getLog(Encryptor.class); + private static byte SEPARATOR = 0; + + private final ThreadLocal threadMac; + + private KeyProvider keyProvider; + private String macAlgorithm; + + /** + * Default constructor for IOC + */ + public MACUtils() + { + threadMac = new ThreadLocal(); + } + + public void setKeyProvider(KeyProvider keyProvider) + { + this.keyProvider = keyProvider; + } + + public void setMacAlgorithm(String macAlgorithm) + { + this.macAlgorithm = macAlgorithm; + } + + protected Mac getMac(String keyAlias) throws Exception + { + Mac mac = threadMac.get(); + if(mac == null) + { + mac = Mac.getInstance(macAlgorithm); + + threadMac.set(mac); + } + Key key = keyProvider.getKey(keyAlias); + if(key == null) + { + throw new AlfrescoRuntimeException("Unexpected null key for key alias " + keyAlias); + } + mac.init(key); + return mac; + } + + protected byte[] longToByteArray(long l) throws IOException + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + dos.writeLong(l); + dos.flush(); + return bos.toByteArray(); + } + + public byte[] generateMAC(String keyAlias, MACInput macInput) + { + try + { + InputStream fullMessage = macInput.getMACInput(); + + if(logger.isDebugEnabled()) + { + logger.debug("Generating MAC for " + macInput + "..."); + } + + Mac mac = getMac(keyAlias); + + byte[] buf = new byte[1024]; + int len; + while((len = fullMessage.read(buf, 0, 1024)) != -1) + { + mac.update(buf, 0, len); + } + byte[] newMAC = mac.doFinal(); + + if(logger.isDebugEnabled()) + { + logger.debug("...done. MAC is " + Arrays.toString(newMAC)); + } + + return newMAC; + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to generate MAC", e); + } + } + + /** + * Compares the expectedMAC against the MAC generated from + * Assumes message has been decrypted + * @param keyAlias String + * @param expectedMAC byte[] + * @param macInput MACInput + * @return boolean + */ + public boolean validateMAC(String keyAlias, byte[] expectedMAC, MACInput macInput) + { + try + { + byte[] mac = generateMAC(keyAlias, macInput); + + if(logger.isDebugEnabled()) + { + logger.debug("Validating expected MAC " + Arrays.toString(expectedMAC) + " against mac " + Arrays.toString(mac) + " for MAC input " + macInput + "..."); + } + + boolean areEqual = Arrays.equals(expectedMAC, mac); + + if(logger.isDebugEnabled()) + { + logger.debug(areEqual ? "...MAC validation succeeded." : "...MAC validation failed."); + } + + return areEqual; + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to validate MAC", e); + } + } + + /** + * Represents the information to be fed into the MAC generator + * + * @since 4.0 + * + */ + public static class MACInput + { + // The message, may be null + private InputStream message; + private long timestamp; + private String ipAddress; + + public MACInput(byte[] message, long timestamp, String ipAddress) + { + this.message = (message != null ? new ByteArrayInputStream(message) : null); + this.timestamp = timestamp; + this.ipAddress = ipAddress; + } + + public InputStream getMessage() + { + return message; + } + + public long getTimestamp() + { + return timestamp; + } + + public String getIpAddress() + { + return ipAddress; + } + + public InputStream getMACInput() throws IOException + { + List inputStreams = new ArrayList(); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bytes); + out.writeUTF(ipAddress); + out.writeByte(SEPARATOR); + out.writeLong(timestamp); + inputStreams.add(new ByteArrayInputStream(bytes.toByteArray())); + + if(message != null) + { + inputStreams.add(message); + } + + return new MessageInputStream(inputStreams); + } + + public String toString() + { + StringBuilder sb = new StringBuilder("MACInput["); + sb.append("timestamp: ").append(getTimestamp()); + sb.append("ipAddress: ").append(getIpAddress()); + return sb.toString(); + } + } + + private static class MessageInputStream extends InputStream + { + private List input; + private InputStream activeInputStream; + private int currentStream = 0; + + public MessageInputStream(List input) + { + this.input = input; + this.currentStream = 0; + this.activeInputStream = input.get(currentStream); + } + + @Override + public void close() throws IOException + { + IOException firstIOException = null; + + for(InputStream in : input) + { + try + { + in.close(); + } + catch(IOException e) + { + if(firstIOException == null) + { + firstIOException = e; + } + } + } + + if(firstIOException != null) + { + throw firstIOException; + } + + } + + @Override + public int read() throws IOException + { + int i = activeInputStream.read(); + if(i == -1) + { + currentStream++; + if(currentStream >= input.size()) + { + return -1; + } + else + { + activeInputStream = input.get(currentStream); + i = activeInputStream.read(); + } + } + + return i; + } + } +} diff --git a/src/main/java/org/alfresco/encryption/MissingKeyException.java b/src/main/java/org/alfresco/encryption/MissingKeyException.java new file mode 100644 index 0000000000..ed55eebb78 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/MissingKeyException.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +/** + * + * @since 4.0 + * + */ +public class MissingKeyException extends Exception +{ + private static final long serialVersionUID = -7843412242954504581L; + + private String keyAlias; + private String keyStoreLocation; + + public MissingKeyException(String message) + { + super(message); + } + + public MissingKeyException(String keyAlias, String keyStoreLocation) + { + // TODO i18n + super("Key " + keyAlias + " is missing from keystore " + keyStoreLocation); + } + + public String getKeyAlias() + { + return keyAlias; + } + + public String getKeyStoreLocation() + { + return keyStoreLocation; + } +} diff --git a/src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java b/src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java new file mode 100644 index 0000000000..cf4b538b9c --- /dev/null +++ b/src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.io.Resource; +import org.springframework.util.ResourceUtils; + +/** + * Loads key resources (key store and key store passwords) from the Spring classpath. + * + * @since 4.0 + * + */ +public class SpringKeyResourceLoader implements KeyResourceLoader, ApplicationContextAware +{ + /** + * The application context might not be available, in which case the usual URL + * loading is used. + */ + private ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + + /** + * Helper method to switch between application context resource loading or + * simpler current classloader resource loading. + */ + private InputStream getSafeInputStream(String location) + { + try + { + final InputStream is; + if (applicationContext != null) + { + Resource resource = applicationContext.getResource(location); + if (resource.exists()) + { + is = new BufferedInputStream(resource.getInputStream()); + } + else + { + // Fall back to conventional loading + File file = ResourceUtils.getFile(location); + if (file.exists()) + { + is = new BufferedInputStream(new FileInputStream(file)); + } + else + { + is = null; + } + } + } + else + { + // Load conventionally (i.e. we are in a unit test) + File file = ResourceUtils.getFile(location); + if (file.exists()) + { + is = new BufferedInputStream(new FileInputStream(file)); + } + else + { + is = null; + } + } + + return is; + } + catch (IOException e) + { + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public InputStream getKeyStore(String keyStoreLocation) + { + if (keyStoreLocation == null) + { + return null; + } + return getSafeInputStream(keyStoreLocation); + } + + /** + * {@inheritDoc} + */ + @Override + public Properties loadKeyMetaData(String keyMetaDataFileLocation) throws IOException + { + if (keyMetaDataFileLocation == null) + { + return null; + } + + try + { + InputStream is = getSafeInputStream(keyMetaDataFileLocation); + if (is == null) + { + return null; + } + else + { + try + { + Properties p = new Properties(); + p.load(is); + return p; + } + finally + { + try { is.close(); } catch (Throwable e) {} + } + } + } + catch(FileNotFoundException e) + { + return null; + } + } +} diff --git a/src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java b/src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java new file mode 100644 index 0000000000..f2259dc10e --- /dev/null +++ b/src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption.ssl; + +/** + *

+ * Signals fatal error in initialization of {@link AuthSSLProtocolSocketFactory}. + *

+ * + *

+ * Adapted from code here: http://svn.apache.org/viewvc/httpcomponents/oac.hc3x/trunk/src/contrib/org/apache/commons/httpclient/contrib/ssl/AuthSSLX509TrustManager.java?revision=608014&view=co + *

+ * + * @since 4.0 + */ +public class AuthSSLInitializationError extends Error +{ + private static final long serialVersionUID = 8135341334029823112L; + + /** + * Creates a new AuthSSLInitializationError. + */ + public AuthSSLInitializationError() + { + super(); + } + + /** + * Creates a new AuthSSLInitializationError with the specified message. + * + * @param message error message + */ + public AuthSSLInitializationError(String message) + { + super(message); + } +} diff --git a/src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java b/src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java new file mode 100644 index 0000000000..d3a08e6b3f --- /dev/null +++ b/src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption.ssl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.security.KeyStore; + +import javax.net.SocketFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; + +import org.alfresco.encryption.AlfrescoKeyStore; +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + *

+ * Mutual Authentication against an Alfresco repository. + * + * AuthSSLProtocolSocketFactory can be used to validate the identity of the HTTPS + * server against a list of trusted certificates and to authenticate to the HTTPS + * server using a private key. + *

+ * + *

+ * Adapted from code here: http://svn.apache.org/viewvc/httpcomponents/oac.hc3x/trunk/src/contrib/org/apache/commons/httpclient/contrib/ssl/AuthSSLX509TrustManager.java?revision=608014&view=co + *

+ * + *

+ * AuthSSLProtocolSocketFactory will enable server authentication when supplied with + * a {@link KeyStore truststore} file containing one or several trusted certificates. + * The client secure socket will reject the connection during the SSL session handshake + * if the target HTTPS server attempts to authenticate itself with a non-trusted + * certificate. + *

+ * + *

+ * AuthSSLProtocolSocketFactory will enable client authentication when supplied with + * a {@link KeyStore keystore} file containg a private key/public certificate pair. + * The client secure socket will use the private key to authenticate itself to the target + * HTTPS server during the SSL session handshake if requested to do so by the server. + * The target HTTPS server will in its turn verify the certificate presented by the client + * in order to establish client's authenticity + *

+ * + * + * @since 4.0 + */ +public class AuthSSLProtocolSocketFactory implements SecureProtocolSocketFactory +{ + /** Log object for this class. */ + private static final Log logger = LogFactory.getLog(AuthSSLProtocolSocketFactory.class); + + private SSLContext sslcontext = null; + + private AlfrescoKeyStore keyStore = null; + private AlfrescoKeyStore trustStore = null; + + /** + * Constructor for AuthSSLProtocolSocketFactory. Either a keystore or truststore file + * must be given. Otherwise SSL context initialization error will result. + * + * @param sslKeyStore SSL parameters to use. + * @param keyResourceLoader loads key resources from an arbitrary source e.g. classpath + */ + public AuthSSLProtocolSocketFactory(AlfrescoKeyStore sslKeyStore, AlfrescoKeyStore sslTrustStore, KeyResourceLoader keyResourceLoader) + { + super(); + this.keyStore = sslKeyStore; + this.trustStore = sslTrustStore; + } + + private SSLContext createSSLContext() + { + KeyManager[] keymanagers = keyStore.createKeyManagers();; + TrustManager[] trustmanagers = trustStore.createTrustManagers(); + + try + { + SSLContext sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(keymanagers, trustmanagers, null); + return sslcontext; + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException("Unable to create SSL context", e); + } + } + + private SSLContext getSSLContext() + { + try + { + if(this.sslcontext == null) + { + this.sslcontext = createSSLContext(); + } + return this.sslcontext; + } + catch(Throwable e) + { + throw new AlfrescoRuntimeException("Unable to create SSL context", e); + } + } + + /** + * Attempts to get a new socket connection to the given host within the given time limit. + *

+ * To circumvent the limitations of older JREs that do not support connect timeout a + * controller thread is executed. The controller thread attempts to create a new socket + * within the given limit of time. If socket constructor does not return until the + * timeout expires, the controller terminates and throws an {@link ConnectTimeoutException} + *

+ * + * @param host the host name/IP + * @param port the port on the host + * @param localAddress the local host name/IP to bind the socket to + * @param localPort the port on the local machine + * @param params {@link HttpConnectionParams Http connection parameters} + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be + * determined + */ + public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, + final HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException + { + SSLSocket sslSocket = null; + + if(params == null) + { + throw new IllegalArgumentException("Parameters may not be null"); + } + int timeout = params.getConnectionTimeout(); + SocketFactory socketfactory = getSSLContext().getSocketFactory(); + if(timeout == 0) + { + sslSocket = (SSLSocket)socketfactory.createSocket(host, port, localAddress, localPort); + } + else + { + sslSocket = (SSLSocket)socketfactory.createSocket(); + SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); + SocketAddress remoteaddr = new InetSocketAddress(host, port); + sslSocket.bind(localaddr); + sslSocket.connect(remoteaddr, timeout); + } + + return sslSocket; + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) + */ + public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) + throws IOException, UnknownHostException + { + SSLSocket sslSocket = (SSLSocket)getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort); + return sslSocket; + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int) + */ + public Socket createSocket(String host, int port) throws IOException, UnknownHostException + { + SSLSocket sslSocket = (SSLSocket)getSSLContext().getSocketFactory().createSocket(host, port); + return sslSocket; + } + + /** + * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean) + */ + public Socket createSocket(Socket socket, String host, int port, boolean autoClose) + throws IOException, UnknownHostException + { + SSLSocket sslSocket = (SSLSocket)getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); + return sslSocket; + } +} diff --git a/src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java b/src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java new file mode 100644 index 0000000000..1252193e94 --- /dev/null +++ b/src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.encryption.ssl; + +import org.alfresco.encryption.KeyStoreParameters; + +/** + * + * @since 4.0 + * + */ +public class SSLEncryptionParameters +{ + private KeyStoreParameters keyStoreParameters; + private KeyStoreParameters trustStoreParameters; + + /** + * Default constructor (for use by Spring) + */ + public SSLEncryptionParameters() + { + super(); + } + + public SSLEncryptionParameters(KeyStoreParameters keyStoreParameters, KeyStoreParameters trustStoreParameters) + { + super(); + this.keyStoreParameters = keyStoreParameters; + this.trustStoreParameters = trustStoreParameters; + } + + public KeyStoreParameters getKeyStoreParameters() + { + return keyStoreParameters; + } + + public KeyStoreParameters getTrustStoreParameters() + { + return trustStoreParameters; + } + + public void setKeyStoreParameters(KeyStoreParameters keyStoreParameters) + { + this.keyStoreParameters = keyStoreParameters; + } + + public void setTrustStoreParameters(KeyStoreParameters trustStoreParameters) + { + this.trustStoreParameters = trustStoreParameters; + } +} diff --git a/src/main/java/org/alfresco/error/AlfrescoRuntimeException.java b/src/main/java/org/alfresco/error/AlfrescoRuntimeException.java new file mode 100644 index 0000000000..af0f059b88 --- /dev/null +++ b/src/main/java/org/alfresco/error/AlfrescoRuntimeException.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.error; + +import java.util.Arrays; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +import org.springframework.extensions.surf.util.I18NUtil; + +import org.alfresco.api.AlfrescoPublicApi; + +/** + * I18n'ed runtime exception thrown by Alfresco code. + * + * @author gavinc + */ +@AlfrescoPublicApi +public class AlfrescoRuntimeException extends RuntimeException +{ + /** + * Serial version UUID + */ + private static final long serialVersionUID = 3787143176461219632L; + + private static final String MESSAGE_DELIMITER = " "; + + private String msgId; + private transient Object[] msgParams = null; + + /** + * Helper factory method making use of variable argument numbers + */ + public static AlfrescoRuntimeException create(String msgId, Object ...objects) + { + return new AlfrescoRuntimeException(msgId, objects); + } + + /** + * Helper factory method making use of variable argument numbers + */ + public static AlfrescoRuntimeException create(Throwable cause, String msgId, Object ...objects) + { + return new AlfrescoRuntimeException(msgId, objects, cause); + } + + /** + * Utility to convert a general Throwable to a RuntimeException. No conversion is done if the + * throwable is already a RuntimeException. + * + * @see #create(Throwable, String, Object...) + */ + public static RuntimeException makeRuntimeException(Throwable e, String msgId, Object ...objects) + { + if (e instanceof RuntimeException) + { + return (RuntimeException) e; + } + // Convert it + return AlfrescoRuntimeException.create(e, msgId, objects); + } + + /** + * Constructor + * + * @param msgId the message id + */ + public AlfrescoRuntimeException(String msgId) + { + super(resolveMessage(msgId, null)); + this.msgId = msgId; + } + + /** + * Constructor + * + * @param msgId the message id + * @param msgParams the message parameters + */ + public AlfrescoRuntimeException(String msgId, Object[] msgParams) + { + super(resolveMessage(msgId, msgParams)); + this.msgId = msgId; + this.msgParams = msgParams; + } + + /** + * Constructor + * + * @param msgId the message id + * @param cause the exception cause + */ + public AlfrescoRuntimeException(String msgId, Throwable cause) + { + super(resolveMessage(msgId, null), cause); + this.msgId = msgId; + } + + /** + * Constructor + * + * @param msgId the message id + * @param msgParams the message parameters + * @param cause the exception cause + */ + public AlfrescoRuntimeException(String msgId, Object[] msgParams, Throwable cause) + { + super(resolveMessage(msgId, msgParams), cause); + this.msgId = msgId; + this.msgParams = msgParams; + } + + /** + * @return the msgId + */ + public String getMsgId() + { + return msgId; + } + + /** + * @return the msgParams + */ + public Object[] getMsgParams() + { + return msgParams; + } + + /** + * @return the numericalId + */ + public String getNumericalId() + { + return getMessage().split(MESSAGE_DELIMITER)[0]; + } + + /** + * Resolves the message id to the localised string. + *

+ * If a localised message can not be found then the message Id is + * returned. + * + * @param messageId the message Id + * @param params message parameters + * @return the localised message (or the message id if none found) + */ + private static String resolveMessage(String messageId, Object[] params) + { + String message = I18NUtil.getMessage(messageId, params); + if (message == null) + { + // If a localized string cannot be found then return the messageId and the params + message = messageId; + if (params != null) + { + message += " - " + Arrays.toString(params); + } + } + return buildErrorLogNumber(message); + } + + /** + * Generate an error log number - based on MMDDXXXX - where M is month, + * D is day and X is an atomic integer count. + * + * @param message Message to prepend the error log number to + * + * @return message with error log number prefix + */ + private static String buildErrorLogNumber(String message) + { + // ensure message is not null + if (message == null) + { + message= ""; + } + + Date today = new Date(); + StringBuilder buf = new StringBuilder(message.length() + 10); + padInt(buf, today.getMonth(), 2); + padInt(buf, today.getDate(), 2); + padInt(buf, errorCounter.getAndIncrement(), 4); + buf.append(MESSAGE_DELIMITER); + buf.append(message); + return buf.toString(); + } + + /** + * Helper to zero pad a number to specified length + */ + private static void padInt(StringBuilder buffer, int value, int length) + { + String strValue = Integer.toString(value); + for (int i = length - strValue.length(); i > 0; i--) + { + buffer.append('0'); + } + buffer.append(strValue); + } + + private static AtomicInteger errorCounter = new AtomicInteger(); + + /** + * Get the root cause. + */ + public Throwable getRootCause() + { + Throwable cause = this; + for (Throwable tmp = this; tmp != null ; tmp = cause.getCause()) + { + cause = tmp; + } + return cause; + } +} diff --git a/src/main/java/org/alfresco/error/ExceptionStackUtil.java b/src/main/java/org/alfresco/error/ExceptionStackUtil.java new file mode 100644 index 0000000000..8ff83c2452 --- /dev/null +++ b/src/main/java/org/alfresco/error/ExceptionStackUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.error; + +/** + * Helper class to provide information about exception stacks. + * + * @author Derek Hulley + */ +public class ExceptionStackUtil +{ + /** + * Searches through the exception stack of the given throwable to find any instance + * of the possible cause. The top-level throwable will also be tested. + * + * @param throwable the exception condition to search + * @param possibleCauses the types of the exception conditions of interest + * @return Returns the first instance that matches one of the given + * possible types, or null if there is nothing in the stack + */ + public static Throwable getCause(Throwable throwable, Class ... possibleCauses) + { + while (throwable != null) + { + for (Class possibleCauseClass : possibleCauses) + { + Class throwableClass = throwable.getClass(); + if (possibleCauseClass.isAssignableFrom(throwableClass)) + { + // We have a match + return throwable; + } + } + // There was no match, so dig deeper + Throwable cause = throwable.getCause(); + throwable = (throwable == cause) ? null : cause; + } + // Nothing found + return null; + } +} diff --git a/src/main/java/org/alfresco/error/StackTraceUtil.java b/src/main/java/org/alfresco/error/StackTraceUtil.java new file mode 100644 index 0000000000..1291226afe --- /dev/null +++ b/src/main/java/org/alfresco/error/StackTraceUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.error; + + +/** + * Helper class around outputting stack traces. + * + * @author Derek Hulley + */ +public class StackTraceUtil +{ + /** + * Builds a message with the stack trace of the form: + *

+     *    SOME MESSAGE:
+     *       Started at:
+     *          com.package...
+     *          com.package...
+     *          ...
+     * 
+ * + * @param msg the initial error message + * @param stackTraceElements the stack trace elements + * @param sb the buffer to append to + * @param maxDepth the maximum number of trace elements to output. 0 or less means output all. + */ + public static void buildStackTrace( + String msg, + StackTraceElement[] stackTraceElements, + StringBuilder sb, + int maxDepth) + { + String lineEnding = System.getProperty("line.separator", "\n"); + + sb.append(msg).append(" ").append(lineEnding) + .append(" Started at: ").append(lineEnding); + for (int i = 0; i < stackTraceElements.length; i++) + { + if (i > maxDepth && maxDepth > 0) + { + sb.append(" ..."); + break; + } + sb.append(" ").append(stackTraceElements[i]); + if (i < stackTraceElements.length - 1) + { + sb.append(lineEnding); + } + } + } +} diff --git a/src/main/java/org/alfresco/hibernate/DialectFactory.java b/src/main/java/org/alfresco/hibernate/DialectFactory.java new file mode 100644 index 0000000000..eb143b8c9d --- /dev/null +++ b/src/main/java/org/alfresco/hibernate/DialectFactory.java @@ -0,0 +1,177 @@ +/* + * 2011 - Alfresco Software, Ltd. + * This file was copied from org.hibernate.dialect.DialectFactory + */ +package org.alfresco.hibernate; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.hibernate.HibernateException; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Dialect; +import org.hibernate.util.ReflectHelper; + +/** + * A factory for generating Dialect instances. + * + * @author Steve Ebersole + * @author Alfresco + */ +public class DialectFactory { + + /** + * Builds an appropriate Dialect instance. + *

+ * If a dialect is explicitly named in the incoming properties, it is used. Otherwise, the database name and version + * (obtained from connection metadata) are used to make the dertemination. + *

+ * An exception is thrown if a dialect was not explicitly set and the database name is not known. + * + * @param props The configuration properties. + * @param databaseName The name of the database product (obtained from metadata). + * @param databaseMajorVersion The major version of the database product (obtained from metadata). + * + * @return The appropriate dialect. + * + * @throws HibernateException No dialect specified and database name not known. + */ + public static Dialect buildDialect(Properties props, String databaseName, int databaseMajorVersion) + throws HibernateException { + String dialectName = props.getProperty( Environment.DIALECT ); + if ( dialectName == null || dialectName.length() == 0) { + return determineDialect( databaseName, databaseMajorVersion ); + } + else { + // Push the dialect onto the system properties + System.setProperty(Environment.DIALECT, dialectName); + return buildDialect( dialectName ); + } + } + + /** + * Determine the appropriate Dialect to use given the database product name + * and major version. + * + * @param databaseName The name of the database product (obtained from metadata). + * @param databaseMajorVersion The major version of the database product (obtained from metadata). + * + * @return An appropriate dialect instance. + */ + public static Dialect determineDialect(String databaseName, int databaseMajorVersion) { + if ( databaseName == null ) { + throw new HibernateException( "Hibernate Dialect must be explicitly set" ); + } + + DatabaseDialectMapper mapper = ( DatabaseDialectMapper ) MAPPERS.get( databaseName ); + if ( mapper == null ) { + throw new HibernateException( "Hibernate Dialect must be explicitly set for database: " + databaseName ); + } + + String dialectName = mapper.getDialectClass( databaseMajorVersion ); + // Push the dialect onto the system properties + System.setProperty(Environment.DIALECT, dialectName); + return buildDialect( dialectName ); + } + + /** + * Returns a dialect instance given the name of the class to use. + * + * @param dialectName The name of the dialect class. + * + * @return The dialect instance. + */ + public static Dialect buildDialect(String dialectName) { + try { + return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance(); + } + catch ( ClassNotFoundException cnfe ) { + throw new HibernateException( "Dialect class not found: " + dialectName ); + } + catch ( Exception e ) { + throw new HibernateException( "Could not instantiate dialect class", e ); + } + } + + /** + * For a given database product name, instances of + * DatabaseDialectMapper know which Dialect to use for different versions. + */ + public static interface DatabaseDialectMapper { + public String getDialectClass(int majorVersion); + } + + /** + * A simple DatabaseDialectMapper for dialects which are independent + * of the underlying database product version. + */ + public static class VersionInsensitiveMapper implements DatabaseDialectMapper { + private String dialectClassName; + + public VersionInsensitiveMapper(String dialectClassName) { + this.dialectClassName = dialectClassName; + } + + public String getDialectClass(int majorVersion) { + return dialectClassName; + } + } + + // TODO : this is the stuff it'd be nice to move to a properties file or some other easily user-editable place + private static final Map MAPPERS = new HashMap(); + static { + // detectors... + MAPPERS.put( "HSQL Database Engine", new VersionInsensitiveMapper( "org.hibernate.dialect.HSQLDialect" ) ); + MAPPERS.put( "H2", new VersionInsensitiveMapper( "org.hibernate.dialect.H2Dialect" ) ); + MAPPERS.put( "MySQL", new VersionInsensitiveMapper( "org.hibernate.dialect.MySQLDialect" ) ); + MAPPERS.put( "PostgreSQL", new VersionInsensitiveMapper( "org.hibernate.dialect.PostgreSQLDialect" ) ); + MAPPERS.put( "Apache Derby", new VersionInsensitiveMapper( "org.hibernate.dialect.DerbyDialect" ) ); + + MAPPERS.put( "Ingres", new VersionInsensitiveMapper( "org.hibernate.dialect.IngresDialect" ) ); + MAPPERS.put( "ingres", new VersionInsensitiveMapper( "org.hibernate.dialect.IngresDialect" ) ); + MAPPERS.put( "INGRES", new VersionInsensitiveMapper( "org.hibernate.dialect.IngresDialect" ) ); + + MAPPERS.put( "Microsoft SQL Server Database", new VersionInsensitiveMapper( "org.hibernate.dialect.SQLServerDialect" ) ); + MAPPERS.put( "Microsoft SQL Server", new VersionInsensitiveMapper( "org.hibernate.dialect.SQLServerDialect" ) ); + MAPPERS.put( "Sybase SQL Server", new VersionInsensitiveMapper( "org.hibernate.dialect.SybaseDialect" ) ); + MAPPERS.put( "Adaptive Server Enterprise", new VersionInsensitiveMapper( "org.hibernate.dialect.SybaseDialect" ) ); + + MAPPERS.put( "Informix Dynamic Server", new VersionInsensitiveMapper( "org.hibernate.dialect.InformixDialect" ) ); + + MAPPERS.put( "DB2/NT", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2/LINUX", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2/6000", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2/HPUX", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2/SUN", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2/LINUX390", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2/AIX64", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/NT",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/NT64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2 UDP",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/LINUX",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/LINUX390",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/LINUXZ64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/400 SQL",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/6000",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2 UDB iSeries",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/AIX64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/HPUX",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/HP64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/SUN",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/SUN64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/PTX",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/2",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + MAPPERS.put( "DB2/LINUXX8664",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); + + MAPPERS.put( "MySQL", new VersionInsensitiveMapper( "org.hibernate.dialect.MySQLInnoDBDialect" ) ); + MAPPERS.put( "DB2/NT64", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "DB2/LINUX", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); + MAPPERS.put( "Microsoft SQL Server Database", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect" ) ); + MAPPERS.put( "Microsoft SQL Server", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect" ) ); + MAPPERS.put( "Sybase SQL Server", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect" ) ); + MAPPERS.put( "Oracle", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect" ) ); + } +} diff --git a/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java b/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java new file mode 100644 index 0000000000..75f6b5f25a --- /dev/null +++ b/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.hibernate; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Dialect; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.orm.hibernate3.LocalSessionFactoryBean; + +/** + * Factory for the Hibernate dialect. Allows dialect detection logic to be centralized and the dialect to be injected + * where required as a singleton from the container. + * + * @author dward + */ +public class DialectFactoryBean implements FactoryBean +{ + + /** The local session factory. */ + private LocalSessionFactoryBean localSessionFactory; + + /** + * Sets the local session factory. + * + * @param localSessionFactory + * the new local session factory + */ + public void setLocalSessionFactory(LocalSessionFactoryBean localSessionFactory) + { + this.localSessionFactory = localSessionFactory; + } + + @SuppressWarnings("deprecation") + @Override + public Dialect getObject() throws SQLException + { + Session session = ((SessionFactory) this.localSessionFactory.getObject()).openSession(); + Configuration cfg = this.localSessionFactory.getConfiguration(); + Connection con = null; + try + { + // make sure that we AUTO-COMMIT + con = session.connection(); + con.setAutoCommit(true); + DatabaseMetaData meta = con.getMetaData(); + Dialect dialect = DialectFactory.buildDialect(cfg.getProperties(), meta.getDatabaseProductName(), meta + .getDatabaseMajorVersion()); + dialect = changeDialect(cfg, dialect); + return dialect; + } + finally + { + try + { + con.close(); + } + catch (Exception e) + { + } + } + } + + /** + * Substitute the dialect with an alternative, if possible. + * + * @param cfg + * the configuration + * @param dialect + * the dialect + * @return the dialect + */ + private Dialect changeDialect(Configuration cfg, Dialect dialect) + { + String dialectName = cfg.getProperty(Environment.DIALECT); + if (dialectName == null || dialectName.length() == 0) + { + // Fix the dialect property to match the detected dialect + cfg.setProperty(Environment.DIALECT, dialect.getClass().getName()); + } + return dialect; + // TODO: https://issues.alfresco.com/jira/browse/ETHREEOH-679 + // else if (dialectName.equals(Oracle9Dialect.class.getName())) + // { + // String subst = AlfrescoOracle9Dialect.class.getName(); + // LogUtil.warn(logger, WARN_DIALECT_SUBSTITUTING, dialectName, subst); + // cfg.setProperty(Environment.DIALECT, subst); + // } + // else if (dialectName.equals(MySQLDialect.class.getName())) + // { + // String subst = MySQLInnoDBDialect.class.getName(); + // LogUtil.warn(logger, WARN_DIALECT_SUBSTITUTING, dialectName, subst); + // cfg.setProperty(Environment.DIALECT, subst); + // } + // else if (dialectName.equals(MySQL5Dialect.class.getName())) + // { + // String subst = MySQLInnoDBDialect.class.getName(); + // LogUtil.warn(logger, WARN_DIALECT_SUBSTITUTING, dialectName, subst); + // cfg.setProperty(Environment.DIALECT, subst); + // } + } + + @Override + public Class getObjectType() + { + return Dialect.class; + } + + @Override + public boolean isSingleton() + { + return true; + } +} diff --git a/src/main/java/org/alfresco/httpclient/AbstractHttpClient.java b/src/main/java/org/alfresco/httpclient/AbstractHttpClient.java new file mode 100644 index 0000000000..0c6e6b6406 --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/AbstractHttpClient.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +import java.io.IOException; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.URI; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public abstract class AbstractHttpClient implements AlfrescoHttpClient +{ + private static final Log logger = LogFactory.getLog(AlfrescoHttpClient.class); + + public static final String ALFRESCO_DEFAULT_BASE_URL = "/alfresco"; + + public static final int DEFAULT_SAVEPOST_BUFFER = 4096; + + // Remote Server access + protected HttpClient httpClient = null; + + private String baseUrl = ALFRESCO_DEFAULT_BASE_URL; + + public AbstractHttpClient(HttpClient httpClient) + { + this.httpClient = httpClient; + } + + protected HttpClient getHttpClient() + { + return httpClient; + } + + /** + * @return the baseUrl + */ + public String getBaseUrl() + { + return baseUrl; + } + + /** + * @param baseUrl the baseUrl to set + */ + public void setBaseUrl(String baseUrl) + { + this.baseUrl = baseUrl; + } + + private boolean isRedirect(HttpMethod method) + { + switch (method.getStatusCode()) { + case HttpStatus.SC_MOVED_TEMPORARILY: + case HttpStatus.SC_MOVED_PERMANENTLY: + case HttpStatus.SC_SEE_OTHER: + case HttpStatus.SC_TEMPORARY_REDIRECT: + if (method.getFollowRedirects()) { + return true; + } else { + return false; + } + default: + return false; + } + } + + /** + * Send Request to the repository + */ + protected HttpMethod sendRemoteRequest(Request req) throws AuthenticationException, IOException + { + if (logger.isDebugEnabled()) + { + logger.debug(""); + logger.debug("* Request: " + req.getMethod() + " " + req.getFullUri() + (req.getBody() == null ? "" : "\n" + new String(req.getBody(), "UTF-8"))); + } + + HttpMethod method = createMethod(req); + + // execute method + executeMethod(method); + + // Deal with redirect + if(isRedirect(method)) + { + Header locationHeader = method.getResponseHeader("location"); + if (locationHeader != null) + { + String redirectLocation = locationHeader.getValue(); + method.setURI(new URI(redirectLocation, true)); + httpClient.executeMethod(method); + } + } + + return method; + } + + protected long executeMethod(HttpMethod method) throws HttpException, IOException + { + // execute method + + long startTime = System.currentTimeMillis(); + + // TODO: Pool, and sent host configuration and state on execution + getHttpClient().executeMethod(method); + + return System.currentTimeMillis() - startTime; + } + + protected HttpMethod createMethod(Request req) throws IOException + { + StringBuilder url = new StringBuilder(128); + url.append(baseUrl); + url.append("/service/"); + url.append(req.getFullUri()); + + // construct method + HttpMethod httpMethod = null; + String method = req.getMethod(); + if(method.equalsIgnoreCase("GET")) + { + GetMethod get = new GetMethod(url.toString()); + httpMethod = get; + httpMethod.setFollowRedirects(true); + } + else if(method.equalsIgnoreCase("POST")) + { + PostMethod post = new PostMethod(url.toString()); + httpMethod = post; + ByteArrayRequestEntity requestEntity = new ByteArrayRequestEntity(req.getBody(), req.getType()); + if (req.getBody().length > DEFAULT_SAVEPOST_BUFFER) + { + post.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE, true); + } + post.setRequestEntity(requestEntity); + // Note: not able to automatically follow redirects for POST, this is handled by sendRemoteRequest + } + else if(method.equalsIgnoreCase("HEAD")) + { + HeadMethod head = new HeadMethod(url.toString()); + httpMethod = head; + httpMethod.setFollowRedirects(true); + } + else + { + throw new AlfrescoRuntimeException("Http Method " + method + " not supported"); + } + + if (req.getHeaders() != null) + { + for (Map.Entry header : req.getHeaders().entrySet()) + { + httpMethod.setRequestHeader(header.getKey(), header.getValue()); + } + } + + return httpMethod; + } + + /* (non-Javadoc) + * @see org.alfresco.httpclient.AlfrescoHttpClient#close() + */ + @Override + public void close() + { + if(httpClient != null) + { + HttpConnectionManager connectionManager = httpClient.getHttpConnectionManager(); + if(connectionManager instanceof MultiThreadedHttpConnectionManager) + { + ((MultiThreadedHttpConnectionManager)connectionManager).shutdown(); + } + } + + } + + + +} diff --git a/src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java b/src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java new file mode 100644 index 0000000000..9629cff1ce --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java @@ -0,0 +1,30 @@ +package org.alfresco.httpclient; + +import java.io.IOException; + +/** + * + * @since 4.0 + * + */ +public interface AlfrescoHttpClient +{ + /** + * Send Request to the repository + */ + public Response sendRequest(Request req) throws AuthenticationException, IOException; + + + /** + * Set the base url to alfresco + * - normally /alfresco + * @param baseUrl + */ + public void setBaseUrl(String baseUrl); + + + /** + * + */ + public void close(); +} diff --git a/src/main/java/org/alfresco/httpclient/AuthenticationException.java b/src/main/java/org/alfresco/httpclient/AuthenticationException.java new file mode 100644 index 0000000000..5f5b084803 --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/AuthenticationException.java @@ -0,0 +1,21 @@ +package org.alfresco.httpclient; + +import org.apache.commons.httpclient.HttpMethod; + +public class AuthenticationException extends Exception +{ + private static final long serialVersionUID = -407003742855571557L; + + private HttpMethod method; + + public AuthenticationException(HttpMethod method) + { + this.method = method; + } + + public HttpMethod getMethod() + { + return method; + } + +} diff --git a/src/main/java/org/alfresco/httpclient/GetRequest.java b/src/main/java/org/alfresco/httpclient/GetRequest.java new file mode 100644 index 0000000000..1960850972 --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/GetRequest.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +/** + * HTTP GET Request + * + * @since 4.0 + */ +public class GetRequest extends Request +{ + public GetRequest(String uri) + { + super("get", uri); + } +} diff --git a/src/main/java/org/alfresco/httpclient/HeadRequest.java b/src/main/java/org/alfresco/httpclient/HeadRequest.java new file mode 100644 index 0000000000..9b8499ffa2 --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/HeadRequest.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +/** + * HTTP HEAD request + * + * @since 4.0 + */ +public class HeadRequest extends Request +{ + public HeadRequest(String uri) + { + super("head", uri); + } +} diff --git a/src/main/java/org/alfresco/httpclient/HttpClientFactory.java b/src/main/java/org/alfresco/httpclient/HttpClientFactory.java new file mode 100644 index 0000000000..1e4054f670 --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/HttpClientFactory.java @@ -0,0 +1,777 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.AlgorithmParameters; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.alfresco.encryption.AlfrescoKeyStore; +import org.alfresco.encryption.AlfrescoKeyStoreImpl; +import org.alfresco.encryption.EncryptionUtils; +import org.alfresco.encryption.Encryptor; +import org.alfresco.encryption.KeyProvider; +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.encryption.KeyStoreParameters; +import org.alfresco.encryption.ssl.AuthSSLProtocolSocketFactory; +import org.alfresco.encryption.ssl.SSLEncryptionParameters; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.Pair; +import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; +import org.apache.commons.httpclient.HostConfiguration; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpHost; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.HttpVersion; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.SimpleHttpConnectionManager; +import org.apache.commons.httpclient.URI; +import org.apache.commons.httpclient.URIException; +import org.apache.commons.httpclient.cookie.CookiePolicy; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.params.DefaultHttpParams; +import org.apache.commons.httpclient.params.DefaultHttpParamsFactory; +import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.httpclient.params.HttpConnectionManagerParams; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.apache.commons.httpclient.params.HttpParams; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; +import org.apache.commons.httpclient.util.DateUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A factory to create HttpClients and AlfrescoHttpClients based on the setting of the 'secureCommsType' property. + * + * @since 4.0 + */ +public class HttpClientFactory +{ + public static enum SecureCommsType + { + HTTPS, NONE; + + public static SecureCommsType getType(String type) + { + if(type.equalsIgnoreCase("https")) + { + return HTTPS; + } + else if(type.equalsIgnoreCase("none")) + { + return NONE; + } + else + { + throw new IllegalArgumentException("Invalid communications type"); + } + } + }; + + private static final Log logger = LogFactory.getLog(HttpClientFactory.class); + + private SSLEncryptionParameters sslEncryptionParameters; + private KeyResourceLoader keyResourceLoader; + private SecureCommsType secureCommsType; + + // for md5 http client (no longer used but kept for now) + private KeyStoreParameters keyStoreParameters; + private MD5EncryptionParameters encryptionParameters; + + private String host; + private int port; + private int sslPort; + + private AlfrescoKeyStore sslKeyStore; + private AlfrescoKeyStore sslTrustStore; + private ProtocolSocketFactory sslSocketFactory; + + private int maxTotalConnections = 40; + + private int maxHostConnections = 40; + + private Integer socketTimeout = null; + + private int connectionTimeout = 0; + + public HttpClientFactory() + { + } + + public HttpClientFactory(SecureCommsType secureCommsType, SSLEncryptionParameters sslEncryptionParameters, + KeyResourceLoader keyResourceLoader, KeyStoreParameters keyStoreParameters, + MD5EncryptionParameters encryptionParameters, String host, int port, int sslPort, int maxTotalConnections, + int maxHostConnections, int socketTimeout) + { + this.secureCommsType = secureCommsType; + this.sslEncryptionParameters = sslEncryptionParameters; + this.keyResourceLoader = keyResourceLoader; + this.keyStoreParameters = keyStoreParameters; + this.encryptionParameters = encryptionParameters; + this.host = host; + this.port = port; + this.sslPort = sslPort; + this.maxTotalConnections = maxTotalConnections; + this.maxHostConnections = maxHostConnections; + this.socketTimeout = socketTimeout; + init(); + } + + public void init() + { + this.sslKeyStore = new AlfrescoKeyStoreImpl(sslEncryptionParameters.getKeyStoreParameters(), keyResourceLoader); + this.sslTrustStore = new AlfrescoKeyStoreImpl(sslEncryptionParameters.getTrustStoreParameters(), keyResourceLoader); + this.sslSocketFactory = new AuthSSLProtocolSocketFactory(sslKeyStore, sslTrustStore, keyResourceLoader); + + // Setup the Apache httpclient library to use our concurrent HttpParams factory + DefaultHttpParams.setHttpParamsFactory(new NonBlockingHttpParamsFactory()); + } + + public void setHost(String host) + { + this.host = host; + } + + public String getHost() + { + return host; + } + + public void setPort(int port) + { + this.port = port; + } + + public int getPort() + { + return port; + } + + public void setSslPort(int sslPort) + { + this.sslPort = sslPort; + } + + public boolean isSSL() + { + return secureCommsType == SecureCommsType.HTTPS; + } + + public void setSecureCommsType(String type) + { + try + { + this.secureCommsType = SecureCommsType.getType(type); + } + catch(IllegalArgumentException e) + { + throw new AlfrescoRuntimeException("", e); + } + } + + public void setSSLEncryptionParameters(SSLEncryptionParameters sslEncryptionParameters) + { + this.sslEncryptionParameters = sslEncryptionParameters; + } + + public void setKeyStoreParameters(KeyStoreParameters keyStoreParameters) + { + this.keyStoreParameters = keyStoreParameters; + } + + public void setEncryptionParameters(MD5EncryptionParameters encryptionParameters) + { + this.encryptionParameters = encryptionParameters; + } + + public void setKeyResourceLoader(KeyResourceLoader keyResourceLoader) + { + this.keyResourceLoader = keyResourceLoader; + } + + /** + * @return the maxTotalConnections + */ + public int getMaxTotalConnections() + { + return maxTotalConnections; + } + + /** + * @param maxTotalConnections the maxTotalConnections to set + */ + public void setMaxTotalConnections(int maxTotalConnections) + { + this.maxTotalConnections = maxTotalConnections; + } + + /** + * @return the maxHostConnections + */ + public int getMaxHostConnections() + { + return maxHostConnections; + } + + /** + * @param maxHostConnections the maxHostConnections to set + */ + public void setMaxHostConnections(int maxHostConnections) + { + this.maxHostConnections = maxHostConnections; + } + + /** + * Attempts to connect to a server will timeout after this period (millis). + * Default is zero (the timeout is not used). + * + * @param connectionTimeout time in millis. + */ + public void setConnectionTimeout(int connectionTimeout) + { + this.connectionTimeout = connectionTimeout; + } + + protected HttpClient constructHttpClient() + { + MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); + HttpClient httpClient = new HttpClient(connectionManager); + HttpClientParams params = httpClient.getParams(); + params.setBooleanParameter(HttpConnectionParams.TCP_NODELAY, true); + params.setBooleanParameter(HttpConnectionParams.STALE_CONNECTION_CHECK, true); + if (socketTimeout != null) + { + params.setSoTimeout(socketTimeout); + } + HttpConnectionManagerParams connectionManagerParams = httpClient.getHttpConnectionManager().getParams(); + connectionManagerParams.setMaxTotalConnections(maxTotalConnections); + connectionManagerParams.setDefaultMaxConnectionsPerHost(maxHostConnections); + connectionManagerParams.setConnectionTimeout(connectionTimeout); + + return httpClient; + } + + protected HttpClient getHttpsClient() + { + return getHttpsClient(host, sslPort); + } + + protected HttpClient getHttpsClient(String httpsHost, int httpsPort) + { + // Configure a custom SSL socket factory that will enforce mutual authentication + HttpClient httpClient = constructHttpClient(); + HttpHostFactory hostFactory = new HttpHostFactory(new Protocol("https", sslSocketFactory, httpsPort)); + httpClient.setHostConfiguration(new HostConfigurationWithHostFactory(hostFactory)); + httpClient.getHostConfiguration().setHost(httpsHost, httpsPort, "https"); + return httpClient; + } + + protected HttpClient getDefaultHttpClient() + { + return getDefaultHttpClient(host, port); + } + + protected HttpClient getDefaultHttpClient(String httpHost, int httpPort) + { + HttpClient httpClient = constructHttpClient(); + httpClient.getHostConfiguration().setHost(httpHost, httpPort); + return httpClient; + } + + protected AlfrescoHttpClient getAlfrescoHttpsClient() + { + AlfrescoHttpClient repoClient = new HttpsClient(getHttpsClient()); + return repoClient; + } + + protected AlfrescoHttpClient getAlfrescoHttpClient() + { + AlfrescoHttpClient repoClient = new DefaultHttpClient(getDefaultHttpClient()); + return repoClient; + } + + protected HttpClient getMD5HttpClient(String host, int port) + { + HttpClient httpClient = constructHttpClient(); + httpClient.getHostConfiguration().setHost(host, port); + return httpClient; + } + + + public AlfrescoHttpClient getRepoClient(String host, int port) + { + AlfrescoHttpClient repoClient = null; + + if(secureCommsType == SecureCommsType.HTTPS) + { + repoClient = getAlfrescoHttpsClient(); + } + else if(secureCommsType == SecureCommsType.NONE) + { + repoClient = getAlfrescoHttpClient(); + } + else + { + throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in alfresco.secureComms, should be 'ssl'or 'none'"); + } + + return repoClient; + } + + public HttpClient getHttpClient() + { + HttpClient httpClient = null; + + if(secureCommsType == SecureCommsType.HTTPS) + { + httpClient = getHttpsClient(); + } + else if(secureCommsType == SecureCommsType.NONE) + { + httpClient = getDefaultHttpClient(); + } + else + { + throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in alfresco.secureComms, should be 'ssl'or 'none'"); + } + + return httpClient; + } + + public HttpClient getHttpClient(String host, int port) + { + HttpClient httpClient = null; + + if(secureCommsType == SecureCommsType.HTTPS) + { + httpClient = getHttpsClient(host, port); + } + else if(secureCommsType == SecureCommsType.NONE) + { + httpClient = getDefaultHttpClient(host, port); + } + else + { + throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in alfresco.secureComms, should be 'ssl'or 'none'"); + } + + return httpClient; + } + + + + /** + * A secure client connection to the repository. + * + * @since 4.0 + * + */ + class HttpsClient extends AbstractHttpClient + { + public HttpsClient(HttpClient httpClient) + { + super(httpClient); + } + + /** + * Send Request to the repository + */ + public Response sendRequest(Request req) throws AuthenticationException, IOException + { + HttpMethod method = super.sendRemoteRequest(req); + return new HttpMethodResponse(method); + } + } + + /** + * Simple HTTP client to connect to the Alfresco server. Simply wraps a HttpClient. + * + * @since 4.0 + */ + class DefaultHttpClient extends AbstractHttpClient + { + public DefaultHttpClient(HttpClient httpClient) + { + super(httpClient); + } + + /** + * Send Request to the repository + */ + public Response sendRequest(Request req) throws AuthenticationException, IOException + { + HttpMethod method = super.sendRemoteRequest(req); + return new HttpMethodResponse(method); + } + } + + + + static class SecureHttpMethodResponse extends HttpMethodResponse + { + protected HostConfiguration hostConfig; + protected EncryptionUtils encryptionUtils; + // Need to get as a byte array because we need to read the request twice, once for authentication + // and again by the web service. + protected byte[] decryptedBody; + + public SecureHttpMethodResponse(HttpMethod method, HostConfiguration hostConfig, + EncryptionUtils encryptionUtils) throws AuthenticationException, IOException + { + super(method); + this.hostConfig = hostConfig; + this.encryptionUtils = encryptionUtils; + + if(method.getStatusCode() == HttpStatus.SC_OK) + { + this.decryptedBody = encryptionUtils.decryptResponseBody(method); + // authenticate the response + if(!authenticate()) + { + throw new AuthenticationException(method); + } + } + } + + protected boolean authenticate() throws IOException + { + return encryptionUtils.authenticateResponse(method, hostConfig.getHost(), decryptedBody); + } + + public InputStream getContentAsStream() throws IOException + { + if(decryptedBody != null) + { + return new ByteArrayInputStream(decryptedBody); + } + else + { + return null; + } + } + } + + private static class HttpHostFactory + { + private Map protocols; + + public HttpHostFactory(Protocol httpsProtocol) + { + protocols = new HashMap(2); + protocols.put("https", httpsProtocol); + } + + /** Get a host for the given parameters. This method need not be thread-safe. */ + public HttpHost getHost(String host, int port, String scheme) + { + if(scheme == null) + { + scheme = "http"; + } + Protocol protocol = protocols.get(scheme); + if(protocol == null) + { + protocol = Protocol.getProtocol("http"); + if(protocol == null) + { + throw new IllegalArgumentException("Unrecognised scheme parameter"); + } + } + + return new HttpHost(host, port, protocol); + } + } + + private static class HostConfigurationWithHostFactory extends HostConfiguration + { + private final HttpHostFactory factory; + + public HostConfigurationWithHostFactory(HttpHostFactory factory) + { + this.factory = factory; + } + + public synchronized void setHost(String host, int port, String scheme) + { + setHost(factory.getHost(host, port, scheme)); + } + + public synchronized void setHost(String host, int port) + { + setHost(factory.getHost(host, port, "http")); + } + + @SuppressWarnings("unused") + public synchronized void setHost(URI uri) + { + try { + setHost(uri.getHost(), uri.getPort(), uri.getScheme()); + } catch(URIException e) { + throw new IllegalArgumentException(e.toString()); + } + } + } + + /** + * An extension of the DefaultHttpParamsFactory that uses a RRW lock pattern rather than + * full synchronization around the parameter CRUD - to avoid locking on many reads. + * + * @author Kevin Roast + */ + public static class NonBlockingHttpParamsFactory extends DefaultHttpParamsFactory + { + private volatile HttpParams httpParams; + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.params.DefaultHttpParamsFactory#getDefaultParams() + */ + @Override + public HttpParams getDefaultParams() + { + if (httpParams == null) + { + synchronized (this) + { + if (httpParams == null) + { + httpParams = createParams(); + } + } + } + + return httpParams; + } + + /** + * NOTE: This is a copy of the code in {@link DefaultHttpParamsFactory} + * Unfortunately this is required because although the factory pattern allows the + * override of the default param creation, it does not allow the class of the actual + * HttpParam implementation to be changed. + */ + @Override + protected HttpParams createParams() + { + HttpClientParams params = new NonBlockingHttpParams(null); + + params.setParameter(HttpMethodParams.USER_AGENT, "Spring Surf via Apache HttpClient/3.1"); + params.setVersion(HttpVersion.HTTP_1_1); + params.setConnectionManagerClass(SimpleHttpConnectionManager.class); + params.setCookiePolicy(CookiePolicy.IGNORE_COOKIES); + params.setHttpElementCharset("US-ASCII"); + params.setContentCharset("ISO-8859-1"); + params.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler()); + + List datePatterns = Arrays.asList( + new String[] { + DateUtil.PATTERN_RFC1123, + DateUtil.PATTERN_RFC1036, + DateUtil.PATTERN_ASCTIME, + "EEE, dd-MMM-yyyy HH:mm:ss z", + "EEE, dd-MMM-yyyy HH-mm-ss z", + "EEE, dd MMM yy HH:mm:ss z", + "EEE dd-MMM-yyyy HH:mm:ss z", + "EEE dd MMM yyyy HH:mm:ss z", + "EEE dd-MMM-yyyy HH-mm-ss z", + "EEE dd-MMM-yy HH:mm:ss z", + "EEE dd MMM yy HH:mm:ss z", + "EEE,dd-MMM-yy HH:mm:ss z", + "EEE,dd-MMM-yyyy HH:mm:ss z", + "EEE, dd-MM-yyyy HH:mm:ss z", + } + ); + params.setParameter(HttpMethodParams.DATE_PATTERNS, datePatterns); + + String agent = null; + try + { + agent = System.getProperty("httpclient.useragent"); + } + catch (SecurityException ignore) + { + } + if (agent != null) + { + params.setParameter(HttpMethodParams.USER_AGENT, agent); + } + + String preemptiveDefault = null; + try + { + preemptiveDefault = System.getProperty("httpclient.authentication.preemptive"); + } + catch (SecurityException ignore) + { + } + if (preemptiveDefault != null) + { + preemptiveDefault = preemptiveDefault.trim().toLowerCase(); + if (preemptiveDefault.equals("true")) + { + params.setParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, Boolean.TRUE); + } + else if (preemptiveDefault.equals("false")) + { + params.setParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, Boolean.FALSE); + } + } + + String defaultCookiePolicy = null; + try + { + defaultCookiePolicy = System.getProperty("apache.commons.httpclient.cookiespec"); + } + catch (SecurityException ignore) + { + } + if (defaultCookiePolicy != null) + { + if ("COMPATIBILITY".equalsIgnoreCase(defaultCookiePolicy)) + { + params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); + } + else if ("NETSCAPE_DRAFT".equalsIgnoreCase(defaultCookiePolicy)) + { + params.setCookiePolicy(CookiePolicy.NETSCAPE); + } + else if ("RFC2109".equalsIgnoreCase(defaultCookiePolicy)) + { + params.setCookiePolicy(CookiePolicy.RFC_2109); + } + } + + return params; + } + } + + /** + * @author Kevin Roast + */ + public static class NonBlockingHttpParams extends HttpClientParams + { + private HashMap parameters = new HashMap(8); + private ReadWriteLock paramLock = new ReentrantReadWriteLock(); + + public NonBlockingHttpParams() + { + super(); + } + + public NonBlockingHttpParams(HttpParams defaults) + { + super(defaults); + } + + @Override + public Object getParameter(final String name) + { + // See if the parameter has been explicitly defined + Object param = null; + paramLock.readLock().lock(); + try + { + param = this.parameters.get(name); + } + finally + { + paramLock.readLock().unlock(); + } + if (param == null) + { + // If not, see if defaults are available + HttpParams defaults = getDefaults(); + if (defaults != null) + { + // Return default parameter value + param = defaults.getParameter(name); + } + } + return param; + } + + @Override + public void setParameter(final String name, final Object value) + { + paramLock.writeLock().lock(); + try + { + this.parameters.put(name, value); + } + finally + { + paramLock.writeLock().unlock(); + } + } + + @Override + public boolean isParameterSetLocally(final String name) + { + paramLock.readLock().lock(); + try + { + return (this.parameters.get(name) != null); + } + finally + { + paramLock.readLock().unlock(); + } + } + + @Override + public void clear() + { + paramLock.writeLock().lock(); + try + { + this.parameters.clear(); + } + finally + { + paramLock.writeLock().unlock(); + } + } + + @Override + public Object clone() throws CloneNotSupportedException + { + NonBlockingHttpParams clone = (NonBlockingHttpParams)super.clone(); + paramLock.readLock().lock(); + try + { + clone.parameters = (HashMap) this.parameters.clone(); + } + finally + { + paramLock.readLock().unlock(); + } + clone.setDefaults(getDefaults()); + return clone; + } + } +} diff --git a/src/main/java/org/alfresco/httpclient/HttpMethodResponse.java b/src/main/java/org/alfresco/httpclient/HttpMethodResponse.java new file mode 100644 index 0000000000..4bf7a9bbbe --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/HttpMethodResponse.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpMethod; + +/** + * + * @since 4.0 + * + */ +public class HttpMethodResponse implements Response +{ + protected HttpMethod method; + + public HttpMethodResponse(HttpMethod method) throws IOException + { + this.method = method; + } + + public void release() + { + method.releaseConnection(); + } + + public InputStream getContentAsStream() throws IOException + { + return method.getResponseBodyAsStream(); + } + + public String getContentType() + { + return getHeader("Content-Type"); + } + + public String getHeader(String name) + { + Header header = method.getResponseHeader(name); + return (header != null) ? header.getValue() : null; + } + + public int getStatus() + { + return method.getStatusCode(); + } + +} diff --git a/src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java b/src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java new file mode 100644 index 0000000000..158b060ad7 --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +/** + * + * @since 4.0 + * + */ +public class MD5EncryptionParameters +{ + private String cipherAlgorithm; + private long messageTimeout; + private String macAlgorithm; + + public MD5EncryptionParameters() + { + + } + + public MD5EncryptionParameters(String cipherAlgorithm, + Long messageTimeout, String macAlgorithm) + { + this.cipherAlgorithm = cipherAlgorithm; + this.messageTimeout = messageTimeout; + this.macAlgorithm = macAlgorithm; + } + + public String getCipherAlgorithm() + { + return cipherAlgorithm; + } + + public void setCipherAlgorithm(String cipherAlgorithm) + { + this.cipherAlgorithm = cipherAlgorithm; + } + + public long getMessageTimeout() + { + return messageTimeout; + } + + public String getMacAlgorithm() + { + return macAlgorithm; + } + + public void setMessageTimeout(long messageTimeout) + { + this.messageTimeout = messageTimeout; + } + + public void setMacAlgorithm(String macAlgorithm) + { + this.macAlgorithm = macAlgorithm; + } +} diff --git a/src/main/java/org/alfresco/httpclient/PostRequest.java b/src/main/java/org/alfresco/httpclient/PostRequest.java new file mode 100644 index 0000000000..08133a12ad --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/PostRequest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +import java.io.UnsupportedEncodingException; + +/** + * HTTP POST Request + * + * @since 4.0 + */ +public class PostRequest extends Request +{ + public PostRequest(String uri, String post, String contentType) + throws UnsupportedEncodingException + { + super("post", uri); + setBody(getEncoding() == null ? post.getBytes() : post.getBytes(getEncoding())); + setType(contentType); + } + + public PostRequest(String uri, byte[] post, String contentType) + { + super("post", uri); + setBody(post); + setType(contentType); + } +} diff --git a/src/main/java/org/alfresco/httpclient/Request.java b/src/main/java/org/alfresco/httpclient/Request.java new file mode 100644 index 0000000000..a6328535c5 --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/Request.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +import java.util.Map; + +/** + * + * @since 4.0 + * + */ +public class Request +{ + private String method; + private String uri; + private Map args; + private Map headers; + private byte[] body; + private String encoding = "UTF-8"; + private String contentType; + + public Request(Request req) + { + this.method = req.method; + this.uri= req.uri; + this.args = req.args; + this.headers = req.headers; + this.body = req.body; + this.encoding = req.encoding; + this.contentType = req.contentType; + } + + public Request(String method, String uri) + { + this.method = method; + this.uri = uri; + } + + public String getMethod() + { + return method; + } + + public String getUri() + { + return uri; + } + + public String getFullUri() + { + // calculate full uri + String fullUri = uri == null ? "" : uri; + if (args != null && args.size() > 0) + { + char prefix = (uri.indexOf('?') == -1) ? '?' : '&'; + for (Map.Entry arg : args.entrySet()) + { + fullUri += prefix + arg.getKey() + "=" + (arg.getValue() == null ? "" : arg.getValue()); + prefix = '&'; + } + } + + return fullUri; + } + + public Request setArgs(Map args) + { + this.args = args; + return this; + } + + public Map getArgs() + { + return args; + } + + public Request setHeaders(Map headers) + { + this.headers = headers; + return this; + } + + public Map getHeaders() + { + return headers; + } + + public Request setBody(byte[] body) + { + this.body = body; + return this; + } + + public byte[] getBody() + { + return body; + } + + public Request setEncoding(String encoding) + { + this.encoding = encoding; + return this; + } + + public String getEncoding() + { + return encoding; + } + + public Request setType(String contentType) + { + this.contentType = contentType; + return this; + } + + public String getType() + { + return contentType; + } +} diff --git a/src/main/java/org/alfresco/httpclient/Response.java b/src/main/java/org/alfresco/httpclient/Response.java new file mode 100644 index 0000000000..59dbae23bf --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/Response.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.httpclient; + +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @since 4.0 + * + */ +public interface Response +{ + public InputStream getContentAsStream() throws IOException; + + public String getHeader(String name); + + public String getContentType(); + + public int getStatus(); + +// public Long getRequestDuration(); + + public void release(); +} diff --git a/src/main/java/org/alfresco/httpclient/SecureHttpClient.java b/src/main/java/org/alfresco/httpclient/SecureHttpClient.java new file mode 100644 index 0000000000..93c93bcd6b --- /dev/null +++ b/src/main/java/org/alfresco/httpclient/SecureHttpClient.java @@ -0,0 +1,149 @@ +package org.alfresco.httpclient; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.AlgorithmParameters; + +import org.alfresco.encryption.EncryptionUtils; +import org.alfresco.encryption.Encryptor; +import org.alfresco.encryption.KeyProvider; +import org.alfresco.encryption.KeyResourceLoader; +import org.alfresco.util.Pair; +import org.apache.commons.httpclient.HostConfiguration; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Simple HTTP client to connect to the Alfresco server. + * + * @since 4.0 + */ +public class SecureHttpClient //extends AbstractHttpClient +{ +// private static final Log logger = LogFactory.getLog(SecureHttpClient.class); +// +// private Encryptor encryptor; +// private EncryptionUtils encryptionUtils; +// private EncryptionService encryptionService; +// private EncryptionParameters encryptionParameters; +// +// /** +// * For testing purposes. +// * +// * @param solrResourceLoader +// * @param alfrescoHost +// * @param alfrescoPort +// * @param encryptionParameters +// */ +// public SecureHttpClient(HttpClientFactory httpClientFactory, String host, int port, EncryptionService encryptionService) +// { +// super(httpClientFactory, host, port); +// this.encryptionUtils = encryptionService.getEncryptionUtils(); +// this.encryptor = encryptionService.getEncryptor(); +// this.encryptionService = encryptionService; +// this.encryptionParameters = encryptionService.getEncryptionParameters(); +// } +// +// public SecureHttpClient(HttpClientFactory httpClientFactory, KeyResourceLoader keyResourceLoader, String host, int port, +// EncryptionParameters encryptionParameters) +// { +// super(httpClientFactory, host, port); +// this.encryptionParameters = encryptionParameters; +// this.encryptionService = new EncryptionService(alfrescoHost, alfrescoPort, keyResourceLoader, encryptionParameters); +// this.encryptionUtils = encryptionService.getEncryptionUtils(); +// this.encryptor = encryptionService.getEncryptor(); +// } +// +// protected HttpMethod createMethod(Request req) throws IOException +// { +// byte[] message = null; +// HttpMethod method = super.createMethod(req); +// +// if(req.getMethod().equalsIgnoreCase("POST")) +// { +// message = req.getBody(); +// // encrypt body +// Pair encrypted = encryptor.encrypt(KeyProvider.ALIAS_SOLR, null, message); +// encryptionUtils.setRequestAlgorithmParameters(method, encrypted.getSecond()); +// +// ByteArrayRequestEntity requestEntity = new ByteArrayRequestEntity(encrypted.getFirst(), "application/octet-stream"); +// ((PostMethod)method).setRequestEntity(requestEntity); +// } +// +// encryptionUtils.setRequestAuthentication(method, message); +// +// return method; +// } +// +// protected HttpMethod sendRemoteRequest(Request req) throws AuthenticationException, IOException +// { +// HttpMethod method = super.sendRemoteRequest(req); +// +// // check that the request returned with an ok status +// if(method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) +// { +// throw new AuthenticationException(method); +// } +// +// return method; +// } +// +// /** +// * Send Request to the repository +// */ +// public Response sendRequest(Request req) throws AuthenticationException, IOException +// { +// HttpMethod method = super.sendRemoteRequest(req); +// return new SecureHttpMethodResponse(method, httpClient.getHostConfiguration(), encryptionUtils); +// } +// +// public static class SecureHttpMethodResponse extends HttpMethodResponse +// { +// protected HostConfiguration hostConfig; +// protected EncryptionUtils encryptionUtils; +// // Need to get as a byte array because we need to read the request twice, once for authentication +// // and again by the web service. +// protected byte[] decryptedBody; +// +// public SecureHttpMethodResponse(HttpMethod method, HostConfiguration hostConfig, +// EncryptionUtils encryptionUtils) throws AuthenticationException, IOException +// { +// super(method); +// this.hostConfig = hostConfig; +// this.encryptionUtils = encryptionUtils; +// +// if(method.getStatusCode() == HttpStatus.SC_OK) +// { +// this.decryptedBody = encryptionUtils.decryptResponseBody(method); +// // authenticate the response +// if(!authenticate()) +// { +// throw new AuthenticationException(method); +// } +// } +// } +// +// protected boolean authenticate() throws IOException +// { +// return encryptionUtils.authenticateResponse(method, hostConfig.getHost(), decryptedBody); +// } +// +// public InputStream getContentAsStream() throws IOException +// { +// if(decryptedBody != null) +// { +// return new ByteArrayInputStream(decryptedBody); +// } +// else +// { +// return null; +// } +// } +// } + +} diff --git a/src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java b/src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java new file mode 100644 index 0000000000..0939129cf3 --- /dev/null +++ b/src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.i18n; + +import java.util.List; + +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Resource bundle bootstrap component. + *

+ * Provides a convenient way to make resource bundles available via Spring config. + * + * @author Roy Wetherall + */ +public class ResourceBundleBootstrapComponent +{ + /** + * Set the resource bundles to be registered. This should be a list of resource + * bundle base names whose content will be made available across the repository. + * + * @param resourceBundles the resource bundles + */ + public void setResourceBundles(List resourceBundles) + { + for (String resourceBundle : resourceBundles) + { + I18NUtil.registerResourceBundle(resourceBundle); + } + } +} diff --git a/src/main/java/org/alfresco/ibatis/BatchingDAO.java b/src/main/java/org/alfresco/ibatis/BatchingDAO.java new file mode 100644 index 0000000000..fc35dfce98 --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/BatchingDAO.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +/** + * Interface for DAOs that offer batching. This should be provided as an optimization + * and DAO implementations that can't supply batching should just do nothing. + * + * @author Derek Hulley + * @since 3.2.1 + */ +public interface BatchingDAO +{ + /** + * Start a batch of insert or update commands + * + * @throws RuntimeException wrapping a SQLException + */ + void startBatch(); + /** + * Write a batch of insert or update commands + * + * @throws RuntimeException wrapping a SQLException + */ + void executeBatch(); +} diff --git a/src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java b/src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java new file mode 100644 index 0000000000..406363569e --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import org.alfresco.ibatis.SerializableTypeHandler.DeserializationException; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.TypeHandler; + +/** + * MyBatis 3.x TypeHandler for _byte[] to BLOB types. + * + * @author sglover + * @since 5.0 + */ +public class ByteArrayTypeHandler implements TypeHandler +{ + /** + * @throws DeserializationException if the object could not be deserialized + */ + public Object getResult(ResultSet rs, String columnName) throws SQLException + { + byte[] ret = null; + try + { + byte[] bytes = rs.getBytes(columnName); + if(bytes != null && !rs.wasNull()) + { + ret = bytes; + } + } + catch (Throwable e) + { + throw new DeserializationException(e); + } + return ret; + } + + @Override + public Object getResult(ResultSet rs, int columnIndex) throws SQLException + { + byte[] ret = null; + try + { + byte[] bytes = rs.getBytes(columnIndex); + if(bytes != null && !rs.wasNull()) + { + ret = bytes; + } + } + catch (Throwable e) + { + throw new DeserializationException(e); + } + return ret; + } + + public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException + { + if (parameter == null) + { + ps.setNull(i, Types.BINARY); + } + else + { + try + { + ps.setBytes(i, (byte[])parameter); + } + catch (Throwable e) + { + throw new SerializationException(e); + } + } + } + + public Object getResult(CallableStatement cs, int columnIndex) throws SQLException + { + throw new UnsupportedOperationException("Unsupported"); + } + + /** + * @return Returns the value given + */ + public Object valueOf(String s) + { + return s; + } + + /** + * Marker exception to allow deserialization issues to be dealt with by calling code. + * If this exception remains uncaught, it will be very difficult to find and rectify + * the data issue. + * + * @author sglover + * @since 5.0 + */ + public static class DeserializationException extends RuntimeException + { + private static final long serialVersionUID = 4673487701048985340L; + + public DeserializationException(Throwable cause) + { + super(cause); + } + } + + /** + * Marker exception to allow serialization issues to be dealt with by calling code. + * Unlike with {@link DeserializationException deserialization}, it is not important + * to handle this exception neatly. + * + * @author sglover + * @since 5.0 + */ + public static class SerializationException extends RuntimeException + { + private static final long serialVersionUID = 962957884262870228L; + + public SerializationException(Throwable cause) + { + super(cause); + } + } +} diff --git a/src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java b/src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java new file mode 100644 index 0000000000..fc056594eb --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import java.io.IOException; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.resource.HierarchicalResourceLoader; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.executor.ErrorContext; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import org.apache.ibatis.mapping.DatabaseIdProvider; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.mapping.VendorDatabaseIdProvider; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.reflection.factory.ObjectFactory; +import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.type.TypeHandler; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.transaction.SpringManagedTransactionFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.NestedIOException; +import org.springframework.core.io.Resource; +import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; + +import static org.springframework.util.Assert.notNull; +import static org.springframework.util.ObjectUtils.isEmpty; +import static org.springframework.util.StringUtils.hasLength; +import static org.springframework.util.StringUtils.tokenizeToStringArray; + +/** + * Extends the MyBatis-Spring support by allowing a choice of {@link org.springframework.core.io.ResourceLoader}. The + * {@link #setResourceLoader(HierarchicalResourceLoader) ResourceLoader} will be used to load the SqlMapConfig + * file and use a {@link HierarchicalXMLConfigBuilder} to read the individual MyBatis (3.x) resources. + *

+ * Pending a better way to extend/override, much of the implementation is a direct copy of the MyBatis-Spring + * {@link SqlSessionFactoryBean}; some of the protected methods do not have access to the object's state + * and can therefore not be overridden successfully. + *

+ * This is equivalent to HierarchicalSqlMapClientFactoryBean which extended iBatis (2.x). + * See also: IBATIS-589 + * and: + * + * @author Derek Hulley, janv + * @since 4.0 + */ +//note: effectively replaces SqlSessionFactoryBean to use hierarchical resource loader +public class HierarchicalSqlSessionFactoryBean extends SqlSessionFactoryBean +{ + + private HierarchicalResourceLoader resourceLoader; + + private final Log logger = LogFactory.getLog(getClass()); + + private Resource configLocation; + + private Resource[] mapperLocations; + + private DataSource dataSource; + + private TransactionFactory transactionFactory; + + private Properties configurationProperties; + + private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); + + private SqlSessionFactory sqlSessionFactory; + + private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1 + + private boolean failFast; + + private Interceptor[] plugins; + + private TypeHandler[] typeHandlers; + + private String typeHandlersPackage; + + private Class[] typeAliases; + + private String typeAliasesPackage; + + private Class typeAliasesSuperType; + + private DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider(); + + private ObjectFactory objectFactory; + + private ObjectWrapperFactory objectWrapperFactory; + /** + * Default constructor + */ + public HierarchicalSqlSessionFactoryBean() + { + } + + /** + * Set the resource loader to use. To use the #resource.dialect# placeholder, use the + * {@link HierarchicalResourceLoader}. + * + * @param resourceLoader the resource loader to use + */ + public void setResourceLoader(HierarchicalResourceLoader resourceLoader) + { + this.resourceLoader = resourceLoader; + } + + /** + * Sets the ObjectFactory. + * + * @since 1.1.2 + * @param objectFactory ObjectFactory + */ + public void setObjectFactory(ObjectFactory objectFactory) { + this.objectFactory = objectFactory; + } + + /** + * Sets the ObjectWrapperFactory. + * + * @since 1.1.2 + * @param objectWrapperFactory ObjectWrapperFactory + */ + public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) { + this.objectWrapperFactory = objectWrapperFactory; + } + + /** + * Sets the DatabaseIdProvider. + * + * @since 1.1.0 + * @return DatabaseIdProvider + */ + public DatabaseIdProvider getDatabaseIdProvider() { + return databaseIdProvider; + } + + /** + * Gets the DatabaseIdProvider + * + * @since 1.1.0 + * @param databaseIdProvider DatabaseIdProvider + */ + public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) { + this.databaseIdProvider = databaseIdProvider; + } + + /** + * Mybatis plugin list. + * + * @since 1.0.1 + * + * @param plugins list of plugins + * + */ + public void setPlugins(Interceptor[] plugins) { + this.plugins = plugins; + } + + /** + * Packages to search for type aliases. + * + * @since 1.0.1 + * + * @param typeAliasesPackage package to scan for domain objects + * + */ + public void setTypeAliasesPackage(String typeAliasesPackage) { + this.typeAliasesPackage = typeAliasesPackage; + } + + /** + * Super class which domain objects have to extend to have a type alias created. + * No effect if there is no package to scan configured. + * + * @since 1.1.2 + * + * @param typeAliasesSuperType super class for domain objects + * + */ + public void setTypeAliasesSuperType(Class typeAliasesSuperType) { + this.typeAliasesSuperType = typeAliasesSuperType; + } + + /** + * Packages to search for type handlers. + * + * @since 1.0.1 + * + * @param typeHandlersPackage package to scan for type handlers + * + */ + public void setTypeHandlersPackage(String typeHandlersPackage) { + this.typeHandlersPackage = typeHandlersPackage; + } + + /** + * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes} + * + * @since 1.0.1 + * + * @param typeHandlers Type handler list + */ + public void setTypeHandlers(TypeHandler[] typeHandlers) { + this.typeHandlers = typeHandlers; + } + + /** + * List of type aliases to register. They can be annotated with {@code Alias} + * + * @since 1.0.1 + * + * @param typeAliases Type aliases list + */ + public void setTypeAliases(Class[] typeAliases) { + this.typeAliases = typeAliases; + } + + /** + * If true, a final check is done on Configuration to assure that all mapped + * statements are fully loaded and there is no one still pending to resolve + * includes. Defaults to false. + * + * @since 1.0.1 + * + * @param failFast enable failFast + */ + public void setFailFast(boolean failFast) { + this.failFast = failFast; + } + + /** + * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is + * "WEB-INF/mybatis-configuration.xml". + */ + public void setConfigLocation(Resource configLocation) { + this.configLocation = configLocation; + } + + /** + * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} + * configuration at runtime. + * + * This is an alternative to specifying "<sqlmapper>" entries in an MyBatis config file. + * This property being based on Spring's resource abstraction also allows for specifying + * resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml". + */ + public void setMapperLocations(Resource[] mapperLocations) { + this.mapperLocations = mapperLocations; + } + + /** + * Set optional properties to be passed into the SqlSession configuration, as alternative to a + * {@code <properties>} tag in the configuration xml file. This will be used to + * resolve placeholders in the config file. + */ + public void setConfigurationProperties(Properties sqlSessionFactoryProperties) { + this.configurationProperties = sqlSessionFactoryProperties; + } + + /** + * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} + * should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same + * JNDI DataSource for both. + * + * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code + * accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}. + * + * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not + * a {@code TransactionAwareDataSourceProxy}. Only data access code may work with + * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the + * underlying target {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} + * passed in, it will be unwrapped to extract its target {@code DataSource}. + * + */ + public void setDataSource(DataSource dataSource) { + if (dataSource instanceof TransactionAwareDataSourceProxy) { + // If we got a TransactionAwareDataSourceProxy, we need to perform + // transactions for its underlying target DataSource, else data + // access code won't see properly exposed transactions (i.e. + // transactions for the target DataSource). + this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); + } else { + this.dataSource = dataSource; + } + } + + /** + * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}. + * + * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By + * default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances. + * + */ + public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) { + this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder; + } + + /** + * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory} + * + * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases: + * be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction, + * SqlSession operations will execute SQL statements non-transactionally. + * + * It is strongly recommended to use the default {@code TransactionFactory}. If not used, any + * attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if + * a transaction is active. + * + * @see SpringManagedTransactionFactory + * @param transactionFactory the MyBatis TransactionFactory + */ + public void setTransactionFactory(TransactionFactory transactionFactory) { + this.transactionFactory = transactionFactory; + } + + /** + * NOTE: This class overrides any {@code Environment} you have set in the MyBatis + * config file. This is used only as a placeholder name. The default value is + * {@code SqlSessionFactoryBean.class.getSimpleName()}. + * + * @param environment the environment name + */ + public void setEnvironment(String environment) { + this.environment = environment; + } + + @Override + public void afterPropertiesSet() throws Exception { + + PropertyCheck.mandatory(this, "resourceLoader", resourceLoader); + + notNull(dataSource, "Property 'dataSource' is required"); + notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); + + this.sqlSessionFactory = buildSqlSessionFactory(); + } + + + + + /** + * Build a {@code SqlSessionFactory} instance. + *

+ * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a + * {@code SqlSessionFactory} instance based on an Reader. + * + * @return SqlSessionFactory + * @throws IOException if loading the config file failed + */ + protected SqlSessionFactory buildSqlSessionFactory() throws IOException { + + Configuration configuration; + + HierarchicalXMLConfigBuilder xmlConfigBuilder = null; + if (this.configLocation != null) { + try { + xmlConfigBuilder = new HierarchicalXMLConfigBuilder(resourceLoader, this.configLocation.getInputStream(), null, this.configurationProperties); + configuration = xmlConfigBuilder.getConfiguration(); + } catch (Exception ex) { + throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); + } finally { + ErrorContext.instance().reset(); + } + } else { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration"); + } + configuration = new Configuration(); + configuration.setVariables(this.configurationProperties); + } + + if (this.objectFactory != null) { + configuration.setObjectFactory(this.objectFactory); + } + + if (this.objectWrapperFactory != null) { + configuration.setObjectWrapperFactory(this.objectWrapperFactory); + } + + if (hasLength(this.typeAliasesPackage)) { + String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, + ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + for (String packageToScan : typeAliasPackageArray) { + configuration.getTypeAliasRegistry().registerAliases(packageToScan, + typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Scanned package: '" + packageToScan + "' for aliases"); + } + } + } + + if (!isEmpty(this.typeAliases)) { + for (Class typeAlias : this.typeAliases) { + configuration.getTypeAliasRegistry().registerAlias(typeAlias); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Registered type alias: '" + typeAlias + "'"); + } + } + } + + if (!isEmpty(this.plugins)) { + for (Interceptor plugin : this.plugins) { + configuration.addInterceptor(plugin); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Registered plugin: '" + plugin + "'"); + } + } + } + + if (hasLength(this.typeHandlersPackage)) { + String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, + ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + for (String packageToScan : typeHandlersPackageArray) { + configuration.getTypeHandlerRegistry().register(packageToScan); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Scanned package: '" + packageToScan + "' for type handlers"); + } + } + } + + if (!isEmpty(this.typeHandlers)) { + for (TypeHandler typeHandler : this.typeHandlers) { + configuration.getTypeHandlerRegistry().register(typeHandler); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Registered type handler: '" + typeHandler + "'"); + } + } + } + + if (xmlConfigBuilder != null) { + try { + xmlConfigBuilder.parse(); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Parsed configuration file: '" + this.configLocation + "'"); + } + } catch (Exception ex) { + throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); + } finally { + ErrorContext.instance().reset(); + } + } + + if (this.transactionFactory == null) { + this.transactionFactory = new SpringManagedTransactionFactory(); + } + + Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource); + configuration.setEnvironment(environment); + + //Commented out to be able to use dummy dataSource in tests. + /* + if (this.databaseIdProvider != null) { + try { + configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); + } catch (SQLException e) { + throw new NestedIOException("Failed getting a databaseId", e); + } + } + */ + + if (!isEmpty(this.mapperLocations)) { + for (Resource mapperLocation : this.mapperLocations) { + if (mapperLocation == null) { + continue; + } + + try { + XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), + configuration, mapperLocation.toString(), configuration.getSqlFragments()); + xmlMapperBuilder.parse(); + } catch (Exception e) { + throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); + } finally { + ErrorContext.instance().reset(); + } + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Parsed mapper file: '" + mapperLocation + "'"); + } + } + } else { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Property 'mapperLocations' was not specified, only MyBatis mapper files specified in the config xml were loaded"); + } + } + + return this.sqlSessionFactoryBuilder.build(configuration); + } + + /** + * {@inheritDoc} + */ + public SqlSessionFactory getObject() throws Exception { + if (this.sqlSessionFactory == null) { + afterPropertiesSet(); + } + + return this.sqlSessionFactory; + } + + /** + * {@inheritDoc} + */ + public Class getObjectType() { + return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass(); + } + + /** + * {@inheritDoc} + */ + public boolean isSingleton() { + return true; + } + + /** + * {@inheritDoc} + */ + public void onApplicationEvent(ApplicationEvent event) { + if (failFast && event instanceof ContextRefreshedEvent) { + // fail-fast -> check all statements are completed + this.sqlSessionFactory.getConfiguration().getMappedStatementNames(); + } + } +} diff --git a/src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java b/src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java new file mode 100644 index 0000000000..89d1ecf3da --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2005-2016 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import java.io.InputStream; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.alfresco.util.resource.HierarchicalResourceLoader; +import org.apache.ibatis.builder.BaseBuilder; +import org.apache.ibatis.builder.BuilderException; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; +import org.apache.ibatis.datasource.DataSourceFactory; +import org.apache.ibatis.executor.ErrorContext; +import org.apache.ibatis.executor.loader.ProxyFactory; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.mapping.DatabaseIdProvider; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.parsing.XNode; +import org.apache.ibatis.parsing.XPathParser; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.reflection.DefaultReflectorFactory; +import org.apache.ibatis.reflection.MetaClass; +import org.apache.ibatis.reflection.ReflectorFactory; +import org.apache.ibatis.reflection.factory.ObjectFactory; +import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; +import org.apache.ibatis.session.AutoMappingBehavior; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.LocalCacheScope; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.type.JdbcType; +import org.springframework.core.io.Resource; + + +/** + * Extends the MyBatis XMLConfigBuilder to allow the selection of a {@link org.springframework.core.io.ResourceLoader} + * that will be used to load the resources specified in the mapper's resource. + *

+ * By using the resource.dialect placeholder with hierarchical resource loading, + * different resource files can be picked up for different dialects. This reduces duplication + * when supporting multiple database configurations. + *

+ * <configuration>
+ *    <mappers>
+ *        <mapper resource="org/x/y/#resource.dialect#/View1.xml"/>
+ *        <mapper resource="org/x/y/#resource.dialect#/View2.xml"/>
+ *    </mappers>
+ * </configuration>
+ * 

+ * + * Much of the implementation is a direct copy of the MyBatis {@link org.apache.ibatis.builder.xml.XMLConfigBuilder}; some + * of the protected methods do not have access to the object's state and can therefore + * not be overridden successfully: IBATIS-589 + + * Pending a better way to extend/override, much of the implementation is a direct copy of the MyBatis + * {@link org.mybatis.spring.SqlSessionFactoryBean}; some of the protected methods do not have access to the object's state + * and can therefore not be overridden successfully. + * + * This is equivalent to HierarchicalSqlMapConfigParser which extended iBatis (2.x). + * See also: IBATIS-589 + * and: + * + * @author Derek Hulley, janv + * @since 4.0 + */ +// note: effectively extends XMLConfigBuilder to use hierarchical resource loader +public class HierarchicalXMLConfigBuilder extends BaseBuilder +{ + private boolean parsed; + private XPathParser parser; + private String environment; + private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); + + // EXTENDED + final private HierarchicalResourceLoader resourceLoader; + + public HierarchicalXMLConfigBuilder(HierarchicalResourceLoader resourceLoader, InputStream inputStream, String environment, Properties props) + { + super(new Configuration()); + + // EXTENDED + this.resourceLoader = resourceLoader; + + ErrorContext.instance().resource("SQL Mapper Configuration"); + this.configuration.setVariables(props); + this.parsed = false; + this.environment = environment; + this.parser = new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()); + } + + public Configuration parse() { + if (parsed) { + throw new BuilderException("Each XMLConfigBuilder can only be used once."); + } + parsed = true; + parseConfiguration(parser.evalNode("/configuration")); + return configuration; + } + + private void parseConfiguration(XNode root) { + try { + //issue #117 read properties first + propertiesElement(root.evalNode("properties")); + typeAliasesElement(root.evalNode("typeAliases")); + pluginElement(root.evalNode("plugins")); + objectFactoryElement(root.evalNode("objectFactory")); + objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); + reflectionFactoryElement(root.evalNode("reflectionFactory")); + settingsElement(root.evalNode("settings")); + // read it after objectFactory and objectWrapperFactory issue #631 + environmentsElement(root.evalNode("environments")); + databaseIdProviderElement(root.evalNode("databaseIdProvider")); + typeHandlerElement(root.evalNode("typeHandlers")); + mapperElement(root.evalNode("mappers")); + } catch (Exception e) { + throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); + } + } + + private void typeAliasesElement(XNode parent) { + if (parent != null) { + for (XNode child : parent.getChildren()) { + if ("package".equals(child.getName())) { + String typeAliasPackage = child.getStringAttribute("name"); + configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); + } else { + String alias = child.getStringAttribute("alias"); + String type = child.getStringAttribute("type"); + try { + Class clazz = Resources.classForName(type); + if (alias == null) { + typeAliasRegistry.registerAlias(clazz); + } else { + typeAliasRegistry.registerAlias(alias, clazz); + } + } catch (ClassNotFoundException e) { + throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); + } + } + } + } + } + + private void pluginElement(XNode parent) throws Exception { + if (parent != null) { + for (XNode child : parent.getChildren()) { + String interceptor = child.getStringAttribute("interceptor"); + Properties properties = child.getChildrenAsProperties(); + Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); + interceptorInstance.setProperties(properties); + configuration.addInterceptor(interceptorInstance); + } + } + } + + private void objectFactoryElement(XNode context) throws Exception { + if (context != null) { + String type = context.getStringAttribute("type"); + Properties properties = context.getChildrenAsProperties(); + ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance(); + factory.setProperties(properties); + configuration.setObjectFactory(factory); + } + } + + private void objectWrapperFactoryElement(XNode context) throws Exception { + if (context != null) { + String type = context.getStringAttribute("type"); + ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance(); + configuration.setObjectWrapperFactory(factory); + } + } + + private void reflectionFactoryElement(XNode context) throws Exception { + if (context != null) { + String type = context.getStringAttribute("type"); + ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance(); + configuration.setReflectorFactory(factory); + } + } + + private void propertiesElement(XNode context) throws Exception { + if (context != null) { + Properties defaults = context.getChildrenAsProperties(); + String resource = context.getStringAttribute("resource"); + String url = context.getStringAttribute("url"); + if (resource != null && url != null) { + throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); + } + if (resource != null) { + defaults.putAll(Resources.getResourceAsProperties(resource)); + } else if (url != null) { + defaults.putAll(Resources.getUrlAsProperties(url)); + } + Properties vars = configuration.getVariables(); + if (vars != null) { + defaults.putAll(vars); + } + parser.setVariables(defaults); + configuration.setVariables(defaults); + } + } + + private void settingsElement(XNode context) throws Exception { + if (context != null) { + Properties props = context.getChildrenAsProperties(); + // Check that all settings are known to the configuration class + MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); + for (Object key : props.keySet()) { + if (!metaConfig.hasSetter(String.valueOf(key))) { + throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); + } + } + configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); + configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); + configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); + configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); + configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true)); + configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); + configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); + configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); + configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); + configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); + configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); + configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); + configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); + configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); + configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); + configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); + configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); + configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); + configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); + configuration.setLogPrefix(props.getProperty("logPrefix")); + configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); + } + } + + private void environmentsElement(XNode context) throws Exception { + if (context != null) { + if (environment == null) { + environment = context.getStringAttribute("default"); + } + for (XNode child : context.getChildren()) { + String id = child.getStringAttribute("id"); + if (isSpecifiedEnvironment(id)) { + TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); + DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); + DataSource dataSource = dsFactory.getDataSource(); + Environment.Builder environmentBuilder = new Environment.Builder(id) + .transactionFactory(txFactory) + .dataSource(dataSource); + configuration.setEnvironment(environmentBuilder.build()); + } + } + } + } + + private void databaseIdProviderElement(XNode context) throws Exception { + DatabaseIdProvider databaseIdProvider = null; + if (context != null) { + String type = context.getStringAttribute("type"); + Properties properties = context.getChildrenAsProperties(); + databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance(); + databaseIdProvider.setProperties(properties); + } + Environment environment = configuration.getEnvironment(); + if (environment != null && databaseIdProvider != null) { + String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); + configuration.setDatabaseId(databaseId); + } + } + + private TransactionFactory transactionManagerElement(XNode context) throws Exception { + if (context != null) { + String type = context.getStringAttribute("type"); + Properties props = context.getChildrenAsProperties(); + TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); + factory.setProperties(props); + return factory; + } + throw new BuilderException("Environment declaration requires a TransactionFactory."); + } + + private DataSourceFactory dataSourceElement(XNode context) throws Exception { + if (context != null) { + String type = context.getStringAttribute("type"); + Properties props = context.getChildrenAsProperties(); + DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); + factory.setProperties(props); + return factory; + } + throw new BuilderException("Environment declaration requires a DataSourceFactory."); + } + + private void typeHandlerElement(XNode parent) throws Exception { + if (parent != null) { + for (XNode child : parent.getChildren()) { + if ("package".equals(child.getName())) { + String typeHandlerPackage = child.getStringAttribute("name"); + typeHandlerRegistry.register(typeHandlerPackage); + } else { + String javaTypeName = child.getStringAttribute("javaType"); + String jdbcTypeName = child.getStringAttribute("jdbcType"); + String handlerTypeName = child.getStringAttribute("handler"); + Class javaTypeClass = resolveClass(javaTypeName); + JdbcType jdbcType = resolveJdbcType(jdbcTypeName); + Class typeHandlerClass = resolveClass(handlerTypeName); + if (javaTypeClass != null) { + if (jdbcType == null) { + typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); + } else { + typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); + } + } else { + typeHandlerRegistry.register(typeHandlerClass); + } + } + } + } + } + + private void mapperElement(XNode parent) throws Exception { + if (parent != null) { + for (XNode child : parent.getChildren()) { + if ("package".equals(child.getName())) { + String mapperPackage = child.getStringAttribute("name"); + configuration.addMappers(mapperPackage); + } else { + String resource = child.getStringAttribute("resource"); + String url = child.getStringAttribute("url"); + String mapperClass = child.getStringAttribute("class"); + if (resource != null && url == null && mapperClass == null) { + ErrorContext.instance().resource(resource); + + // // EXTENDED + // inputStream = Resources.getResourceAsStream(resource); + InputStream inputStream = null; + Resource res = resourceLoader.getResource(resource); + if (res != null && res.exists()) + { + inputStream = res.getInputStream(); + } + else { + throw new BuilderException("Failed to get resource: "+resource); + } + + //InputStream inputStream = Resources.getResourceAsStream(resource); + XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); + mapperParser.parse(); + } else if (resource == null && url != null && mapperClass == null) { + ErrorContext.instance().resource(url); + InputStream inputStream = Resources.getUrlAsStream(url); + XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); + mapperParser.parse(); + } else if (resource == null && url == null && mapperClass != null) { + Class mapperInterface = Resources.classForName(mapperClass); + configuration.addMapper(mapperInterface); + } else { + throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); + } + } + } + } + } + + private boolean isSpecifiedEnvironment(String id) { + if (environment == null) { + throw new BuilderException("No environment specified."); + } else if (id == null) { + throw new BuilderException("Environment requires an id attribute."); + } else if (environment.equals(id)) { + return true; + } + return false; + } +} diff --git a/src/main/java/org/alfresco/ibatis/IdsEntity.java b/src/main/java/org/alfresco/ibatis/IdsEntity.java new file mode 100644 index 0000000000..d8f4f09e2c --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/IdsEntity.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import java.util.List; + +/** + * Entity bean to carry ID-style information + * + * @author Derek Hulley + * @since 3.2 + */ +public class IdsEntity +{ + private Long idOne; + private Long idTwo; + private Long idThree; + private Long idFour; + private List ids; + public Long getIdOne() + { + return idOne; + } + public void setIdOne(Long id) + { + this.idOne = id; + } + public Long getIdTwo() + { + return idTwo; + } + public void setIdTwo(Long id) + { + this.idTwo = id; + } + public Long getIdThree() + { + return idThree; + } + public void setIdThree(Long idThree) + { + this.idThree = idThree; + } + public Long getIdFour() + { + return idFour; + } + public void setIdFour(Long idFour) + { + this.idFour = idFour; + } + public List getIds() + { + return ids; + } + public void setIds(List ids) + { + this.ids = ids; + } +} diff --git a/src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java b/src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java new file mode 100644 index 0000000000..6b92b5bf6c --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A helper that runs a unit of work, transparently retrying the unit of work if + * an error occurs. + *

+ * Defaults: + *

+ * + * @author Derek Hulley + * @since 3.4 + */ +public class RetryingCallbackHelper +{ + private static final Log logger = LogFactory.getLog(RetryingCallbackHelper.class); + + /** The maximum number of retries. -1 for infinity. */ + private int maxRetries; + /** How much time to wait with each retry. */ + private int retryWaitMs; + + /** + * Callback interface + * @author Derek Hulley + */ + public interface RetryingCallback + { + /** + * Perform a unit of work. + * + * @return Return the result of the unit of work + * @throws Throwable This can be anything and will guarantee either a retry or a rollback + */ + public Result execute() throws Throwable; + }; + + /** + * Default constructor. + */ + public RetryingCallbackHelper() + { + this.maxRetries = 5; + this.retryWaitMs = 10; + } + + /** + * Set the maximimum number of retries. -1 for infinity. + */ + public void setMaxRetries(int maxRetries) + { + this.maxRetries = maxRetries; + } + + public void setRetryWaitMs(int retryWaitMs) + { + this.retryWaitMs = retryWaitMs; + } + + /** + * Execute a callback until it succeeds, fails or until a maximum number of retries have + * been attempted. + * + * @param callback The callback containing the unit of work. + * @return Returns the result of the unit of work. + * @throws RuntimeException all checked exceptions are converted + */ + public R doWithRetry(RetryingCallback callback) + { + // Track the last exception caught, so that we can throw it if we run out of retries. + RuntimeException lastException = null; + for (int count = 0; count == 0 || count < maxRetries; count++) + { + try + { + // Do the work. + R result = callback.execute(); + if (logger.isDebugEnabled()) + { + if (count != 0) + { + logger.debug("\n" + + "Retrying work succeeded: \n" + + " Thread: " + Thread.currentThread().getName() + "\n" + + " Iteration: " + count); + } + } + return result; + } + catch (Throwable e) + { + lastException = (e instanceof RuntimeException) ? + (RuntimeException) e : + new AlfrescoRuntimeException("Exception in Transaction.", e); + if (logger.isDebugEnabled()) + { + logger.debug("\n" + + "Retrying work failed: \n" + + " Thread: " + Thread.currentThread().getName() + "\n" + + " Iteration: " + count + "\n" + + " Exception follows:", + e); + } + else if (logger.isInfoEnabled()) + { + String msg = String.format( + "Retrying %s: count %2d; wait: %3dms; msg: \"%s\"; exception: (%s)", + Thread.currentThread().getName(), + count, retryWaitMs, + e.getMessage(), + e.getClass().getName()); + logger.info(msg); + } + try + { + Thread.sleep(retryWaitMs); + } + catch (InterruptedException ie) + { + // Do nothing. + } + // Try again + continue; + } + } + // We've worn out our welcome and retried the maximum number of times. + // So, fail. + throw lastException; + } +} diff --git a/src/main/java/org/alfresco/ibatis/RollupResultHandler.java b/src/main/java/org/alfresco/ibatis/RollupResultHandler.java new file mode 100644 index 0000000000..4d2419e46a --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/RollupResultHandler.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.ibatis.executor.result.DefaultResultContext; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.ResultContext; +import org.apache.ibatis.session.ResultHandler; +import org.mybatis.spring.SqlSessionTemplate; + +/** + * A {@link ResultHandler} that collapses multiple rows based on a set of properties. + *

+ * This class is derived from earlier RollupRowHandler used to workaround the groupBy and nested ResultMap + * behaviour in iBatis (2.3.4.726) IBATIS-503. + *

+ * The set of properties given act as a unique key. When the unique key changes, the collection + * values from the nested ResultMap are coalesced and the given {@link ResultHandler} is called. It is + * possible to embed several instances of this handler for deeply-nested ResultMap declarations. + *

+ * Use this instance as a regular {@link ResultHandler}, but with one big exception: call {@link #processLastResults()} + * after executing the SQL statement. Remove the groupBy attribute from the iBatis ResultMap + * declaration. + *

+ * Example iBatis 2.x (TODO migrate example to MyBatis 3.x): + *

+    <resultMap id="result_AuditQueryAllValues"
+               extends="alfresco.audit.result_AuditQueryNoValues"
+               class="AuditQueryResult">
+        <result property="auditValues" resultMap="alfresco.propval.result_PropertyIdSearchRow"/>
+    </resultMap>
+ * 
+ * Example usage: + *
+        RowHandler rowHandler = new RowHandler()
+        {
+            public void handleRow(Object valueObject)
+            {
+                // DO SOMETHING
+            }
+        };
+        RollupRowHandler rollupRowHandler = new RollupRowHandler(
+                new String[] {"auditEntryId"},
+                "auditValues",
+                rowHandler,
+                maxResults);
+        
+        if (maxResults > 0)
+        {
+            // Calculate the maximum results required
+            int sqlMaxResults = (maxResults > 0 ? ((maxResults+1) * 20) : Integer.MAX_VALUE);
+            
+            List rows = template.queryForList(SELECT_ENTRIES_WITH_VALUES, params, 0, sqlMaxResults);
+            for (AuditQueryResult row : rows)
+            {
+                rollupRowHandler.handleRow(row);
+            }
+            // Don't process last result:
+            //    rollupRowHandler.processLastResults();
+            //    The last result may be incomplete
+        }
+        else
+        {
+            template.queryWithRowHandler(SELECT_ENTRIES_WITH_VALUES, params, rollupRowHandler);
+            rollupRowHandler.processLastResults();
+        }
+ * 
+ *

+ * This class is not thread-safe; use a new instance for each use. + * + * @author Derek Hulley, janv + * @since 4.0 + */ +public class RollupResultHandler implements ResultHandler +{ + private final String[] keyProperties; + private final String collectionProperty; + private final ResultHandler resultHandler; + private final int maxResults; + + private Object[] lastKeyValues; + private List rawResults; + private int resultCount; + + private Configuration configuration; + + /** + * @param keyProperties the properties that make up the unique key + * @param collectionProperty the property mapped using a nested ResultMap + * @param resultHandler the result handler that will receive the rolled-up results + */ + public RollupResultHandler(Configuration configuration, String[] keyProperties, String collectionProperty, ResultHandler resultHandler) + { + this(configuration, keyProperties, collectionProperty, resultHandler, Integer.MAX_VALUE); + } + + /** + * @param keyProperties the properties that make up the unique key + * @param collectionProperty the property mapped using a nested ResultMap + * @param resultHandler the result handler that will receive the rolled-up results + * @param maxResults the maximum number of results to retrieve (-1 for no limit). + * Make sure that the query result limit is large enough to produce this + * at least this number of results + */ + public RollupResultHandler(Configuration configuration, String[] keyProperties, String collectionProperty, ResultHandler resultHandler, int maxResults) + { + if (keyProperties == null || keyProperties.length == 0) + { + throw new IllegalArgumentException("RollupRowHandler can only be used with at least one key property."); + } + if (collectionProperty == null) + { + throw new IllegalArgumentException("RollupRowHandler must have a collection property."); + } + this.configuration = configuration; + this.keyProperties = keyProperties; + this.collectionProperty = collectionProperty; + this.resultHandler = resultHandler; + this.maxResults = maxResults; + this.rawResults = new ArrayList(100); + } + + public void handleResult(ResultContext context) + { + // Shortcut if we have processed enough results + if (maxResults > 0 && resultCount >= maxResults) + { + return; + } + + Object valueObject = context.getResultObject(); + MetaObject probe = configuration.newMetaObject(valueObject); + + // Check if the key has changed + if (lastKeyValues == null) + { + lastKeyValues = getKeyValues(probe); + resultCount = 0; + } + // Check if it has changed + Object[] currentKeyValues = getKeyValues(probe); + if (!Arrays.deepEquals(lastKeyValues, currentKeyValues)) + { + // Key has changed, so handle the results + Object resultObject = coalesceResults(configuration, rawResults, collectionProperty); + if (resultObject != null) + { + DefaultResultContext resultContext = new DefaultResultContext(); + resultContext.nextResultObject(resultObject); + + resultHandler.handleResult(resultContext); + resultCount++; + } + rawResults.clear(); + lastKeyValues = currentKeyValues; + } + // Add the new value to the results for next time + rawResults.add(valueObject); + // Done + } + + /** + * Client code must call this method once the query returns so that the final results + * can be passed to the inner RowHandler. If a query is limited by size, then it is + * possible that the unprocessed results represent an incomplete final object; in this case + * it would be best to ignore the last results. If the query is complete (i.e. all results + * are returned) then this method should be called. + *

+ * If you want X results and each result is made up of N rows (on average), then set the query + * limit to:
+ * L = X * (N+1)
+ * and don't call this method. + */ + public void processLastResults() + { + // Shortcut if we have processed enough results + if (maxResults > 0 && resultCount >= maxResults) + { + return; + } + // Handle any outstanding results + Object resultObject = coalesceResults(configuration, rawResults, collectionProperty); + if (resultObject != null) + { + DefaultResultContext resultContext = new DefaultResultContext(); + resultContext.nextResultObject(resultObject); + + resultHandler.handleResult(resultContext); + resultCount++; + rawResults.clear(); // Stop it from being used again + } + } + + @SuppressWarnings("unchecked") + private static Object coalesceResults(Configuration configuration, List valueObjects, String collectionProperty) + { + // Take the first result as the base value + Object resultObject = null; + MetaObject probe = null; + Collection collection = null; + for (Object object : valueObjects) + { + if (collection == null) + { + resultObject = object; + probe = configuration.newMetaObject(resultObject); + collection = (Collection) probe.getValue(collectionProperty); + } + else + { + Collection addedValues = (Collection) probe.getValue(collectionProperty); + collection.addAll(addedValues); + } + } + // Done + return resultObject; + } + + /** + * @return Returns the values for the {@link RollupResultHandler#keyProperties} + */ + private Object[] getKeyValues(MetaObject probe) + { + Object[] keyValues = new Object[keyProperties.length]; + for (int i = 0; i < keyProperties.length; i++) + { + keyValues[i] = probe.getValue(keyProperties[i]); + } + return keyValues; + } +} diff --git a/src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java b/src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java new file mode 100644 index 0000000000..094264cae2 --- /dev/null +++ b/src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.TypeHandler; + +/** + * MyBatis 3.x TypeHandler for java.io.Serializable to BLOB types. + * + * @author Derek Hulley, janv + * @since 4.0 + */ +public class SerializableTypeHandler implements TypeHandler +{ + public static final int DEFAULT_SERIALIZABLE_TYPE = Types.LONGVARBINARY; + private static volatile int serializableType = DEFAULT_SERIALIZABLE_TYPE; + + /** + * @see Types + */ + public static void setSerializableType(int serializableType) + { + SerializableTypeHandler.serializableType = serializableType; + } + + /** + * @return Returns the SQL type to use for serializable columns + */ + public static int getSerializableType() + { + return serializableType; + } + + /** + * @throws DeserializationException if the object could not be deserialized + */ + public Object getResult(ResultSet rs, String columnName) throws SQLException + { + final Serializable ret; + try + { + InputStream is = rs.getBinaryStream(columnName); + if (is == null || rs.wasNull()) + { + return null; + } + // Get the stream and deserialize + ObjectInputStream ois = new ObjectInputStream(is); + Object obj = ois.readObject(); + // Success + ret = (Serializable) obj; + } + catch (Throwable e) + { + throw new DeserializationException(e); + } + return ret; + } + + @Override + public Object getResult(ResultSet rs, int columnIndex) throws SQLException + { + final Serializable ret; + try + { + InputStream is = rs.getBinaryStream(columnIndex); + if (is == null || rs.wasNull()) + { + return null; + } + // Get the stream and deserialize + ObjectInputStream ois = new ObjectInputStream(is); + Object obj = ois.readObject(); + // Success + ret = (Serializable) obj; + } + catch (Throwable e) + { + throw new DeserializationException(e); + } + return ret; + } + + public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException + { + if (parameter == null) + { + ps.setNull(i, SerializableTypeHandler.serializableType); + } + else + { + try + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(parameter); + byte[] bytes = baos.toByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ps.setBinaryStream(i, bais, bytes.length); + } + catch (Throwable e) + { + throw new SerializationException(e); + } + } + } + + public Object getResult(CallableStatement cs, int columnIndex) throws SQLException + { + throw new UnsupportedOperationException("Unsupported"); + } + + /** + * @return Returns the value given + */ + public Object valueOf(String s) + { + return s; + } + + /** + * Marker exception to allow deserialization issues to be dealt with by calling code. + * If this exception remains uncaught, it will be very difficult to find and rectify + * the data issue. + * + * @author Derek Hulley + * @since 3.2 + */ + public static class DeserializationException extends RuntimeException + { + private static final long serialVersionUID = 4673487701048985340L; + + public DeserializationException(Throwable cause) + { + super(cause); + } + } + + /** + * Marker exception to allow serialization issues to be dealt with by calling code. + * Unlike with {@link DeserializationException deserialization}, it is not important + * to handle this exception neatly. + * + * @author Derek Hulley + * @since 3.2 + */ + public static class SerializationException extends RuntimeException + { + private static final long serialVersionUID = 962957884262870228L; + + public SerializationException(Throwable cause) + { + super(cause); + } + } +} diff --git a/src/main/java/org/alfresco/processor/Processor.java b/src/main/java/org/alfresco/processor/Processor.java new file mode 100644 index 0000000000..52c7e61c1a --- /dev/null +++ b/src/main/java/org/alfresco/processor/Processor.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.processor; + +/** + * Interface for Proccessor classes - such as Template or Scripting Processors. + * + * @author Roy Wetherall + */ +public interface Processor +{ + /** + * Get the name of the processor + * + * @return the name of the processor + */ + public String getName(); + + /** + * The file extension that the processor is associated with, null if none. + * + * @return the extension + */ + public String getExtension(); + + /** + * Registers a processor extension with the processor + * + * @param processorExtension the process extension + */ + public void registerProcessorExtension(ProcessorExtension processorExtension); +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/processor/ProcessorExtension.java b/src/main/java/org/alfresco/processor/ProcessorExtension.java new file mode 100644 index 0000000000..c9dbb97e07 --- /dev/null +++ b/src/main/java/org/alfresco/processor/ProcessorExtension.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.processor; + +/** + * Interface to represent a server side script implementation + * + * @author Roy Wetherall + */ +public interface ProcessorExtension +{ + /** + * Returns the name of the extension + * + * @return the name of the extension + */ + String getExtensionName(); +} diff --git a/src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java b/src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java new file mode 100644 index 0000000000..832ee3a350 --- /dev/null +++ b/src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.List; + +/** + * Caching support extension for {@link CannedQueryFactory} implementations. + *

+ * Depending on the parameters provided, this class may choose to pick up existing results + * and re-use them for later page requests; the client will not have knowledge of the + * shortcuts. + * + * TODO: This is work-in-progress + * + * @author Derek Hulley + * @since 4.0 + */ +public abstract class AbstractCachingCannedQueryFactory extends AbstractCannedQueryFactory +{ + /** + * Base implementation that provides a caching facade around the query. + * + * @return a decoraded facade query that will cache query results for later paging requests + */ + @Override + public final CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + throw new UnsupportedOperationException(); + } + + /** + * Derived classes must implement this method to provide the raw query that supports the given + * parameters. All requests must be serviced without any further caching in order to prevent + * duplicate caching. + * + * @param parameters the query parameters as given by the client + * @return the query that will generate the results + */ + protected abstract CannedQuery getCannedQueryImpl(CannedQueryParameters parameters); + + private class CannedQueryCacheFacade extends AbstractCannedQuery + { + private final AbstractCannedQuery delegate; + + private CannedQueryCacheFacade(CannedQueryParameters params, AbstractCannedQuery delegate) + { + super(params); + this.delegate = delegate; + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + // Copy the parameters and remove all references to paging. + // The underlying query will return full or filtered results (possibly also sorted) + // but will not apply page limitations + + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/main/java/org/alfresco/query/AbstractCannedQuery.java b/src/main/java/org/alfresco/query/AbstractCannedQuery.java new file mode 100644 index 0000000000..a8effe90fc --- /dev/null +++ b/src/main/java/org/alfresco/query/AbstractCannedQuery.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.GUID; +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; + +/** + * Basic support for canned query implementations. + * + * @author Derek Hulley + * @since 4.0 + */ +public abstract class AbstractCannedQuery implements CannedQuery +{ + private final CannedQueryParameters parameters; + private final String queryExecutionId; + private CannedQueryResults results; + + /** + * Construct the canned query given the original parameters applied. + *

+ * A random GUID query execution ID will be generated. + * + * @param parameters the original query parameters + */ + protected AbstractCannedQuery(CannedQueryParameters parameters) + { + ParameterCheck.mandatory("parameters", parameters); + this.parameters = parameters; + this.queryExecutionId = GUID.generate(); + } + + @Override + public CannedQueryParameters getParameters() + { + return parameters; + } + + @Override + public String toString() + { + return "AbstractCannedQuery [parameters=" + parameters + ", class=" + this.getClass() + "]"; + } + + @Override + public synchronized final CannedQueryResults execute() + { + // Check that we are not requerying + if (results != null) + { + throw new IllegalStateException( + "This query instance has already by used." + + " It can only be used to query once."); + } + + // Get the raw query results + List rawResults = queryAndFilter(parameters); + if (rawResults == null) + { + throw new AlfrescoRuntimeException("Execution returned 'null' results"); + } + + // Apply sorting + if (isApplyPostQuerySorting()) + { + rawResults = applyPostQuerySorting(rawResults, parameters.getSortDetails()); + } + + // Apply permissions + if (isApplyPostQueryPermissions()) + { + // Work out the number of results required + int requestedCount = parameters.getResultsRequired(); + rawResults = applyPostQueryPermissions(rawResults, requestedCount); + } + + // Get total count + final Pair totalCount = getTotalResultCount(rawResults); + + // Apply paging + CannedQueryPageDetails pagingDetails = parameters.getPageDetails(); + List> pages = Collections.singletonList(rawResults); + if (isApplyPostQueryPaging()) + { + pages = applyPostQueryPaging(rawResults, pagingDetails); + } + + // Construct results object + final List> finalPages = pages; + + // Has more items beyond requested pages ? ... ie. at least one more page (with at least one result) + final boolean hasMoreItems = (rawResults.size() > pagingDetails.getResultsRequiredForPaging()); + + results = new CannedQueryResults() + { + @Override + public CannedQuery getOriginatingQuery() + { + return AbstractCannedQuery.this; + } + + @Override + public String getQueryExecutionId() + { + return queryExecutionId; + } + + @Override + public Pair getTotalResultCount() + { + if (parameters.getTotalResultCountMax() > 0) + { + return totalCount; + } + else + { + throw new IllegalStateException("Total results were not requested in parameters."); + } + } + + @Override + public int getPagedResultCount() + { + int finalPagedCount = 0; + for (List page : finalPages) + { + finalPagedCount += page.size(); + } + return finalPagedCount; + } + + @Override + public int getPageCount() + { + return finalPages.size(); + } + + @Override + public R getSingleResult() + { + if (finalPages.size() != 1 && finalPages.get(0).size() != 1) + { + throw new IllegalStateException("There must be exactly one page of one result available."); + } + return finalPages.get(0).get(0); + } + + @Override + public List getPage() + { + if (finalPages.size() != 1) + { + throw new IllegalStateException("There must be exactly one page of results available."); + } + return finalPages.get(0); + } + + @Override + public List> getPages() + { + return finalPages; + } + + @Override + public boolean hasMoreItems() + { + return hasMoreItems; + } + }; + return results; + } + + /** + * Implement the basic query, returning either filtered or all results. + *

+ * The implementation may optimally select, filter, sort and apply permissions. + * If not, however, the subsequent post-query methods + * ({@link #applyPostQuerySorting(List, CannedQuerySortDetails)}, + * {@link #applyPostQueryPermissions(List, int)} and + * {@link #applyPostQueryPaging(List, CannedQueryPageDetails)}) can + * be used to trim the results as required. + * + * @param parameters the full parameters to be used for execution + */ + protected abstract List queryAndFilter(CannedQueryParameters parameters); + + /** + * Override to get post-query calls to do sorting. + * + * @return true to get a post-query call to sort (default false) + */ + protected boolean isApplyPostQuerySorting() + { + return false; + } + + /** + * Called before {@link #applyPostQueryPermissions(List, int)} to allow the results to be sorted prior to permission checks. + * Note that the query implementation may optimally sort results during retrieval, in which case this method does not need to be implemented. + * + * @param results the results to sort + * @param sortDetails details of the sorting requirements + * @return the results according to the new sort order + */ + protected List applyPostQuerySorting(List results, CannedQuerySortDetails sortDetails) + { + throw new UnsupportedOperationException("Override this method if post-query sorting is required."); + } + + /** + * Override to get post-query calls to apply permission filters. + * + * @return true to get a post-query call to apply permissions (default false) + */ + protected boolean isApplyPostQueryPermissions() + { + return false; + } + + /** + * Called after the query to filter out results based on permissions. + * Note that the query implementation may optimally only select results + * based on available privileges, in which case this method does not need to be implemented. + *

+ * Permission evaluations should continue until the requested number of results are retrieved + * or all available results have been examined. + * + * @param results the results to apply permissions to + * @param requestedCount the minimum number of results to pass the permission checks + * in order to fully satisfy the paging requirements + * @return the remaining results (as a single "page") after permissions have been applied + */ + protected List applyPostQueryPermissions(List results, int requestedCount) + { + throw new UnsupportedOperationException("Override this method if post-query filtering is required."); + } + + /** + * Get the total number of available results after querying, filtering, sorting and permission checking. + *

+ * The default implementation assumes that the given results are the final total possible. + * + * @param results the results after filtering and sorting, but before paging + * @return pair representing (a) the total number of results and + * (b) the estimated (or actual) number of maximum results + * possible for this query. + * + * @see CannedQueryParameters#getTotalResultCountMax() + */ + protected Pair getTotalResultCount(List results) + { + Integer size = results.size(); + return new Pair(size, size); + } + + /** + * Override to get post-query calls to do pull out paged results. + * + * @return true to get a post-query call to page (default true) + */ + protected boolean isApplyPostQueryPaging() + { + return true; + } + + /** + * Called after the {@link #applyPostQuerySorting(List, CannedQuerySortDetails) sorting phase} to pull out results specific + * to the required pages. Note that the query implementation may optimally + * create page-specific results, in which case this method does not need to be implemented. + *

+ * The base implementation assumes that results are not paged and that the current results + * are all the available results i.e. that paging still needs to be applied. + * + * @param results full results (all or excess pages) + * @param pageDetails details of the paging requirements + * @return the specific page of results as per the query parameters + */ + protected List> applyPostQueryPaging(List results, CannedQueryPageDetails pageDetails) + { + int skipResults = pageDetails.getSkipResults(); + int pageSize = pageDetails.getPageSize(); + int pageCount = pageDetails.getPageCount(); + int pageNumber = pageDetails.getPageNumber(); + + int availableResults = results.size(); + int totalResults = pageSize * pageCount; + int firstResult = skipResults + ((pageNumber-1) * pageSize); // first of window + + List> pages = new ArrayList>(pageCount); + + // First some shortcuts + if (skipResults == 0 && pageSize > availableResults) + { + return Collections.singletonList(results); // Requesting more results in one page than are available + } + else if (firstResult > availableResults) + { + return pages; // Start of first page is after all results + } + + // Build results + Iterator iterator = results.listIterator(firstResult); + int countTotal = 0; + List page = new ArrayList(Math.min(results.size(), pageSize)); // Prevent memory blow-out + pages.add(page); + while (iterator.hasNext() && countTotal < totalResults) + { + if (page.size() == pageSize) + { + // Create a page and add it to the results + page = new ArrayList(pageSize); + pages.add(page); + } + R next = iterator.next(); + page.add(next); + + countTotal++; + } + + // Done + return pages; + } +} diff --git a/src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java b/src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java new file mode 100644 index 0000000000..86efc92e93 --- /dev/null +++ b/src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.registry.NamedObjectRegistry; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.InitializingBean; + +/** + * Basic services for {@link CannedQueryFactory} implementations. + * + * @author Derek Hulley + * @since 4.0 + */ +public abstract class AbstractCannedQueryFactory implements CannedQueryFactory, InitializingBean, BeanNameAware +{ + private String name; + @SuppressWarnings("rawtypes") + private NamedObjectRegistry registry; + + /** + * Set the name with which to {@link #setRegistry(NamedObjectRegistry) register} + * @param name the name of the bean + */ + public void setBeanName(String name) + { + this.name = name; + } + + /** + * Set the registry with which to register + */ + @SuppressWarnings("rawtypes") + public void setRegistry(NamedObjectRegistry registry) + { + this.registry = registry; + } + + /** + * Registers the instance + */ + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "name", name); + PropertyCheck.mandatory(this, "registry", registry); + + registry.register(name, this); + } + + /** + * Helper method to construct a unique query execution ID based on the + * instance of the factory and the parameters provided. + * + * @param parameters the query parameters + * @return a unique query instance ID + */ + protected String getQueryExecutionId(CannedQueryParameters parameters) + { + // Create a GUID + String uuid = name + "-" + GUID.generate(); + return uuid; + } + + /** + * {@inheritDoc} + */ + @Override + public CannedQuery getCannedQuery(Object parameterBean, int skipResults, int pageSize, String queryExecutionId) + { + return getCannedQuery(new CannedQueryParameters(parameterBean, skipResults, pageSize, queryExecutionId)); + } +} diff --git a/src/main/java/org/alfresco/query/CannedQuery.java b/src/main/java/org/alfresco/query/CannedQuery.java new file mode 100644 index 0000000000..f6b7b55cd5 --- /dev/null +++ b/src/main/java/org/alfresco/query/CannedQuery.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +/** + * Interface for named query implementations. These are queries that encapsulate varying + * degrees of functionality, but ultimately provide support for paging results. + *

+ * Note that each instance of the query is stateful and cannot be reused. + * + * @param the query result type + * + * @author Derek Hulley + * @since 4.0 + */ +public interface CannedQuery +{ + /** + * Get the original parameters used to generate the query. + * + * @return the parameters used to obtain the named query. + */ + CannedQueryParameters getParameters(); + + /** + * Execute the named query, which was provided to support the + * {@link #getParameters() parameters} originally provided. + *

+ * Note: This method can only be used once; to requery, get a new + * instance from the {@link CannedQueryFactory factory}. + * + * @return the query results + * + * @throws IllegalStateException on second and subsequent calls to this method + */ + CannedQueryResults execute(); +} diff --git a/src/main/java/org/alfresco/query/CannedQueryException.java b/src/main/java/org/alfresco/query/CannedQueryException.java new file mode 100644 index 0000000000..1950c169fd --- /dev/null +++ b/src/main/java/org/alfresco/query/CannedQueryException.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Exception generated by failures to execute canned queries. + * + * @author Derek Hulley + * @since 4.0 + */ +public class CannedQueryException extends AlfrescoRuntimeException +{ + private static final long serialVersionUID = -4985399145374964458L; + + /** + * @param msg the message + */ + public CannedQueryException(String msg) + { + super(msg); + } + + /** + * @param msg the message + * @param cause the exception cause + */ + public CannedQueryException(String msg, Throwable cause) + { + super(msg, cause); + } + + /** + * @param msgId the message id + * @param msgParams the message parameters + */ + public CannedQueryException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + /** + * @param msgId the message id + * @param msgParams the message parameters + * @param cause the exception cause + */ + public CannedQueryException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } +} diff --git a/src/main/java/org/alfresco/query/CannedQueryFactory.java b/src/main/java/org/alfresco/query/CannedQueryFactory.java new file mode 100644 index 0000000000..56e7716026 --- /dev/null +++ b/src/main/java/org/alfresco/query/CannedQueryFactory.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +/** + * Interface for factory implementations for producing instances of {@link CannedQuery} + * based on all the query parameters. + * + * @param the query result type + * + * @author Derek Hulley, janv + * @since 4.0 + */ +public interface CannedQueryFactory +{ + /** + * Retrieve an instance of a {@link CannedQuery} based on the full range of + * available parameters. + * + * @param parameters the full query parameters + * @return an implementation that will execute the query + */ + CannedQuery getCannedQuery(CannedQueryParameters parameters); + + /** + * Retrieve an instance of a {@link CannedQuery} based on limited parameters. + * + * @param parameterBean the values that the query will be based on or null + * if not relevant to the query + * @param skipResults results to skip before page + * @param pageSize the size of page - ie. max items (if skipResults = 0) + * @param queryExecutionId ID of a previously-executed query to be used during follow-up + * page requests - null if not available + * @return an implementation that will execute the query + */ + CannedQuery getCannedQuery(Object parameterBean, int skipResults, int pageSize, String queryExecutionId); +} diff --git a/src/main/java/org/alfresco/query/CannedQueryPageDetails.java b/src/main/java/org/alfresco/query/CannedQueryPageDetails.java new file mode 100644 index 0000000000..56520b3a5e --- /dev/null +++ b/src/main/java/org/alfresco/query/CannedQueryPageDetails.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +/** + * Details for canned queries supporting paged results. + *

+ * Results are {@link #skipResults skipped}, chopped into pages of + * {@link #pageSize appropriate size} before the {@link #pageCount start page} + * and {@link #pageNumber number} are returned. + * + * @author Derek Hulley + * @since 4.0 + */ +public class CannedQueryPageDetails +{ + public static final int DEFAULT_SKIP_RESULTS = 0; + public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + public static final int DEFAULT_PAGE_NUMBER = 1; + public static final int DEFAULT_PAGE_COUNT = 1; + + private final int skipResults; + private final int pageSize; + private final int pageNumber; + private final int pageCount; + + /** + * Construct with defaults + *

    + *
  • skipResults: {@link #DEFAULT_SKIP_RESULTS}
  • + *
  • pageSize: {@link #DEFAULT_PAGE_SIZE}
  • + *
  • pageNumber: {@link #DEFAULT_PAGE_NUMBER}
  • + *
  • pageCount: {@link #DEFAULT_PAGE_COUNT}
  • + *
+ */ + public CannedQueryPageDetails() + { + this(DEFAULT_SKIP_RESULTS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_COUNT); + } + + /** + * Construct with defaults + *
    + *
  • pageNumber: {@link #DEFAULT_PAGE_NUMBER}
  • + *
  • pageCount: {@link #DEFAULT_PAGE_COUNT}
  • + *
+ * @param skipResults results to skip before page one + * (default {@link #DEFAULT_SKIP_RESULTS}) + * @param pageSize the size of each page + * (default {@link #DEFAULT_PAGE_SIZE}) + */ + public CannedQueryPageDetails(int skipResults, int pageSize) + { + this (skipResults, pageSize, DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_COUNT); + } + + /** + * @param skipResults results to skip before page one + * (default {@link #DEFAULT_SKIP_RESULTS}) + * @param pageSize the size of each page + * (default {@link #DEFAULT_PAGE_SIZE}) + * @param pageNumber the first page number to return + * (default {@link #DEFAULT_PAGE_NUMBER}) + * @param pageCount the number of pages to return + * (default {@link #DEFAULT_PAGE_COUNT}) + */ + public CannedQueryPageDetails(int skipResults, int pageSize, int pageNumber, int pageCount) + { + this.skipResults = skipResults; + this.pageSize = pageSize; + this.pageNumber = pageNumber; + this.pageCount = pageCount; + + // Do some checks + if (skipResults < 0) + { + throw new IllegalArgumentException("Cannot skip fewer than 0 results."); + } + if (pageSize < 1) + { + throw new IllegalArgumentException("pageSize must be greater than zero."); + } + if (pageNumber < 1) + { + throw new IllegalArgumentException("pageNumber must be greater than zero."); + } + if (pageCount < 1) + { + throw new IllegalArgumentException("pageCount must be greater than zero."); + } + } + + /** + * Helper constructor to transform a paging request into the Canned Query form. + * + * @param pagingRequest the paging details + */ + public CannedQueryPageDetails(PagingRequest pagingRequest) + { + this(pagingRequest.getSkipCount(), pagingRequest.getMaxItems()); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("NamedQueryPageDetails ") + .append("[skipResults=").append(skipResults) + .append(", pageSize=").append(pageSize) + .append(", pageCount=").append(pageCount) + .append(", pageNumber=").append(pageNumber) + .append("]"); + return sb.toString(); + } + + /** + * Get the number of query results to skip before applying further page parameters + * @return results to skip before page one + */ + public int getSkipResults() + { + return skipResults; + } + + /** + * Get the size of each page + * @return the size of each page + */ + public int getPageSize() + { + return pageSize; + } + + /** + * Get the first page number to return + * @return the first page number to return + */ + public int getPageNumber() + { + return pageNumber; + } + + /** + * Get the total number of pages to return + * @return the number of pages to return + */ + public int getPageCount() + { + return pageCount; + } + + /** + * Calculate the number of results that would be required to satisy this paging request. + * Note that the skip size can significantly increase this number even if the page sizes + * are small. + * + * @return the number of results required for proper paging + */ + public int getResultsRequiredForPaging() + { + int tmp = skipResults + pageCount * pageSize; + if(tmp < 0) + { + // overflow + return Integer.MAX_VALUE; + } + else + { + return tmp; + } + } +} diff --git a/src/main/java/org/alfresco/query/CannedQueryParameters.java b/src/main/java/org/alfresco/query/CannedQueryParameters.java new file mode 100644 index 0000000000..7554ac2862 --- /dev/null +++ b/src/main/java/org/alfresco/query/CannedQueryParameters.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +/** + * Parameters defining the {@link CannedQuery named query} to execute. + *

+ * The implementations of the underlying queries may be vastly different + * depending on seemingly-minor variations in the parameters; only set the + * parameters that are required. + * + * @author Derek Hulley + * @since 4.0 + */ +public class CannedQueryParameters +{ + public static final int DEFAULT_TOTAL_COUNT_MAX = 0; // default 0 => don't request total count + + private final Object parameterBean; + private final CannedQueryPageDetails pageDetails; + private final CannedQuerySortDetails sortDetails; + private final int totalResultCountMax; + private final String queryExecutionId; + + /** + *

    + *
  • pageDetails: null
  • + *
  • sortDetails: null
  • + *
  • totalResultCountMax: 0
  • + *
  • queryExecutionId: null
  • + *
+ * + */ + public CannedQueryParameters(Object parameterBean) + { + this (parameterBean, null, null, DEFAULT_TOTAL_COUNT_MAX, null); + } + + /** + * Defaults: + *
    + *
  • pageDetails.pageNumber: 1
  • + *
  • pageDetails.pageCount: 1
  • + *
  • totalResultCountMax: 0
  • + *
+ * + */ + public CannedQueryParameters( + Object parameterBean, + int skipResults, + int pageSize, + String queryExecutionId) + { + this ( + parameterBean, + new CannedQueryPageDetails(skipResults, pageSize, CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT), + null, + DEFAULT_TOTAL_COUNT_MAX, + queryExecutionId); + } + + /** + * Defaults: + *
    + *
  • totalResultCountMax: 0
  • + *
  • queryExecutionId: null
  • + *
+ * + */ + public CannedQueryParameters( + Object parameterBean, + CannedQueryPageDetails pageDetails, + CannedQuerySortDetails sortDetails) + { + this (parameterBean, pageDetails, sortDetails, DEFAULT_TOTAL_COUNT_MAX, null); + } + + /** + * Construct all the parameters for executing a named query, using values from the + * {@link PagingRequest}. + * + * @param parameterBean the values that the query will be based on or null + * if not relevant to the query + * @param sortDetails the type of sorting to be applied or null for none + * @param pagingRequest the type of paging to be applied or null for none + */ + public CannedQueryParameters( + Object parameterBean, + CannedQuerySortDetails sortDetails, + PagingRequest pagingRequest) + { + this ( + parameterBean, + pagingRequest == null ? null : new CannedQueryPageDetails(pagingRequest), + sortDetails, + pagingRequest == null ? 0 : pagingRequest.getRequestTotalCountMax(), + pagingRequest == null ? null : pagingRequest.getQueryExecutionId()); + } + + /** + * Construct all the parameters for executing a named query. Note that the allowable values + * for the arguments depend on the specific query being executed. + * + * @param parameterBean the values that the query will be based on or null + * if not relevant to the query + * @param pageDetails the type of paging to be applied or null for none + * @param sortDetails the type of sorting to be applied or null for none + * @param totalResultCountMax greater than zero if the query should not only return the required rows + * but should also return the total number of possible rows up to + * the given maximum. + * @param queryExecutionId ID of a previously-executed query to be used during follow-up + * page requests - null if not available + */ + @SuppressWarnings("unchecked") + public CannedQueryParameters( + Object parameterBean, + CannedQueryPageDetails pageDetails, + CannedQuerySortDetails sortDetails, + int totalResultCountMax, + String queryExecutionId) + { + if (totalResultCountMax < 0) + { + throw new IllegalArgumentException("totalResultCountMax cannot be negative."); + } + + this.parameterBean = parameterBean; + this.pageDetails = pageDetails == null ? new CannedQueryPageDetails() : pageDetails; + this.sortDetails = sortDetails == null ? new CannedQuerySortDetails() : sortDetails; + this.totalResultCountMax = totalResultCountMax; + this.queryExecutionId = queryExecutionId; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("NamedQueryParameters ") + .append("[parameterBean=").append(parameterBean) + .append(", pageDetails=").append(pageDetails) + .append(", sortDetails=").append(sortDetails) + .append(", requestTotalResultCountMax=").append(totalResultCountMax) + .append(", queryExecutionId=").append(queryExecutionId) + .append("]"); + return sb.toString(); + } + + public String getQueryExecutionId() + { + return queryExecutionId; + } + + /** + * @return the sort details (never null) + */ + public CannedQuerySortDetails getSortDetails() + { + return sortDetails; + } + + /** + * @return the query paging details (never null) + */ + public CannedQueryPageDetails getPageDetails() + { + return pageDetails; + } + + /** + * @return if > 0 then the query should not only return the required rows but should + * also return the total count (number of possible rows) up to the given max + * if 0 then query does not need to return the total count + */ + public int getTotalResultCountMax() + { + return totalResultCountMax; + } + + /** + * Helper method to get the total number of query results that need to be obtained in order + * to satisfy the {@link #getPageDetails() paging requirements}, the + * maximum result count ... and an extra to provide + * 'hasMore' functionality. + * + * @return the minimum number of results required before pages can be created + */ + public int getResultsRequired() + { + int resultsForPaging = pageDetails.getResultsRequiredForPaging(); + if (resultsForPaging < Integer.MAX_VALUE) // Add one for 'hasMore' + { + resultsForPaging++; + } + int maxRequired = Math.max(totalResultCountMax, resultsForPaging); + return maxRequired; + } + + /** + * @return parameterBean the values that the query will be based on or null + * if not relevant to the query + */ + public Object getParameterBean() + { + return parameterBean; + } +} diff --git a/src/main/java/org/alfresco/query/CannedQueryResults.java b/src/main/java/org/alfresco/query/CannedQueryResults.java new file mode 100644 index 0000000000..1dda96309e --- /dev/null +++ b/src/main/java/org/alfresco/query/CannedQueryResults.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.List; + +/** + * Interface for results returned by {@link CannedQuery canned queries}. + * + * @author Derek Hulley, janv + * @since 4.0 + */ +public interface CannedQueryResults extends PagingResults +{ + /** + * Get the instance of the query that generated these results. + * + * @return the query that generated these results. + */ + CannedQuery getOriginatingQuery(); + + /** + * Get the total number of results available within the pages of this result. + * The count excludes results chopped out by the paging process i.e. it is only + * the count of results physically obtainable through this instance. + * + * @return number of results available in the pages + */ + int getPagedResultCount(); + + /** + * Get the number of pages available + * + * @return the number of pages available + */ + int getPageCount(); + + /** + * Get a single result if there is only one result expected. + * + * @return a single result + * @throws IllegalStateException if the query returned more than one result + */ + R getSingleResult(); + + /** + * Get the paged results + * + * @return a list of paged results + */ + List> getPages(); +} diff --git a/src/main/java/org/alfresco/query/CannedQuerySortDetails.java b/src/main/java/org/alfresco/query/CannedQuerySortDetails.java new file mode 100644 index 0000000000..134c4a176f --- /dev/null +++ b/src/main/java/org/alfresco/query/CannedQuerySortDetails.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.alfresco.util.Pair; + +/** + * Details for canned queries supporting sorted results + * + * @author Derek Hulley + * @since 4.0 + */ +public class CannedQuerySortDetails +{ + /** + * Sort ordering for the sort pairs. + * @author Derek Hulley + * @since 4.0 + */ + public static enum SortOrder + { + ASCENDING, + DESCENDING + } + + private final List> sortPairs; + + /** + * Construct the sort details with a variable number of sort pairs. + *

+ * Sorting is done by:
+ * key: the key type to sort on
+ * sortOrder: the ordering of values associated with the key
+ * + * @param sortPairs the sort pairs, which will be applied in order + */ + public CannedQuerySortDetails(Pair ... sortPairs) + { + this.sortPairs = Collections.unmodifiableList(Arrays.asList(sortPairs)); + } + + /** + * Construct the sort details from a list of sort pairs. + *

+ * Sorting is done by:
+ * key: the key type to sort on
+ * sortOrder: the ordering of values associated with the key
+ * + * @param sortPairs the sort pairs, which will be applied in order + */ + public CannedQuerySortDetails(List> sortPairs) + { + this.sortPairs = Collections.unmodifiableList(sortPairs); + } + + @Override + public String toString() + { + return "CannedQuerySortDetails [sortPairs=" + sortPairs + "]"; + } + + /** + * Get the sort definitions. The instance will become unmodifiable after this has been called. + */ + public List> getSortPairs() + { + return Collections.unmodifiableList(sortPairs); + } +} diff --git a/src/main/java/org/alfresco/query/EmptyCannedQueryResults.java b/src/main/java/org/alfresco/query/EmptyCannedQueryResults.java new file mode 100644 index 0000000000..547b419a7f --- /dev/null +++ b/src/main/java/org/alfresco/query/EmptyCannedQueryResults.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.Collections; +import java.util.List; + +import org.alfresco.util.Pair; + +/** + * An always empty {@link CannedQueryResults}, used when you know + * you can short circuit a query when no results are found. + * + * @author Nick Burch + * @since 4.0 + */ +public class EmptyCannedQueryResults extends EmptyPagingResults implements CannedQueryResults +{ + private CannedQuery query; + + public EmptyCannedQueryResults(CannedQuery query) + { + this.query = query; + } + + @Override + public CannedQuery getOriginatingQuery() { + return query; + } + + @Override + public int getPageCount() { + return 0; + } + + @Override + public int getPagedResultCount() { + return 0; + } + + @Override + public List> getPages() { + return Collections.emptyList(); + } + + @Override + public R getSingleResult() { + return null; + } +} diff --git a/src/main/java/org/alfresco/query/EmptyPagingResults.java b/src/main/java/org/alfresco/query/EmptyPagingResults.java new file mode 100644 index 0000000000..995605adf5 --- /dev/null +++ b/src/main/java/org/alfresco/query/EmptyPagingResults.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.Collections; +import java.util.List; + +import org.alfresco.util.Pair; + +/** + * An always empty {@link PagingResults}, used when you know + * you can short circuit a query when no results are found. + * + * @author Nick Burch + * @since 4.0 + */ +public class EmptyPagingResults implements PagingResults +{ + /** + * Returns an empty page + */ + public List getPage() + { + return Collections.emptyList(); + } + + /** + * No more items remain + */ + public boolean hasMoreItems() + { + return false; + } + + /** + * There are no results + */ + public Pair getTotalResultCount() + { + return new Pair(0,0); + } + + /** + * There is no unique query ID, as no query was done + */ + public String getQueryExecutionId() + { + return null; + } +} diff --git a/src/main/java/org/alfresco/query/ListBackedPagingResults.java b/src/main/java/org/alfresco/query/ListBackedPagingResults.java new file mode 100644 index 0000000000..a2833cc67d --- /dev/null +++ b/src/main/java/org/alfresco/query/ListBackedPagingResults.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.Collections; +import java.util.List; + +import org.alfresco.util.Pair; + +/** + * Wraps a list of items as a {@link PagingResults}, used typically when + * migrating from a full listing system to a paged one. + * + * @author Nick Burch + * @since Odin + */ +public class ListBackedPagingResults implements PagingResults +{ + private List results; + private int size; + private boolean hasMore; + + public ListBackedPagingResults(List list) + { + this.results = Collections.unmodifiableList(list); + + // No more items remain, the page is everything + size = list.size(); + hasMore = false; + } + public ListBackedPagingResults(List list, PagingRequest paging) + { + // Excerpt + int start = paging.getSkipCount(); + int end = Math.min(list.size(), start + paging.getMaxItems()); + if (paging.getMaxItems() == 0) + { + end = list.size(); + } + + this.results = Collections.unmodifiableList( + list.subList(start, end)); + this.size = list.size(); + this.hasMore = ! (list.size() == end); + } + + /** + * Returns the whole set of results as one page + */ + public List getPage() + { + return results; + } + + public boolean hasMoreItems() + { + return hasMore; + } + + /** + * We know exactly how many results there are + */ + public Pair getTotalResultCount() + { + return new Pair(size, size); + } + + /** + * There is no unique query ID, as no query was done + */ + public String getQueryExecutionId() + { + return null; + } +} diff --git a/src/main/java/org/alfresco/query/PageDetails.java b/src/main/java/org/alfresco/query/PageDetails.java new file mode 100644 index 0000000000..c26fec0634 --- /dev/null +++ b/src/main/java/org/alfresco/query/PageDetails.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +/** + * Stores paging details based on a PagingRequest. + * + * @author steveglover + * + */ +public class PageDetails +{ + private boolean hasMoreItems = false; + private int pageSize; + private int skipCount; + private int maxItems; + private int end; + + public PageDetails(int pageSize, boolean hasMoreItems, int skipCount, int maxItems, int end) + { + super(); + this.hasMoreItems = hasMoreItems; + this.pageSize = pageSize; + this.skipCount = skipCount; + this.maxItems = maxItems; + this.end = end; + } + + public int getSkipCount() + { + return skipCount; + } + + public int getMaxItems() + { + return maxItems; + } + + public int getEnd() + { + return end; + } + + public boolean hasMoreItems() + { + return hasMoreItems; + } + + public int getPageSize() + { + return pageSize; + } + + public static PageDetails getPageDetails(PagingRequest pagingRequest, int totalSize) + { + int skipCount = pagingRequest.getSkipCount(); + int maxItems = pagingRequest.getMaxItems(); + int end = skipCount + maxItems; + int pageSize = -1; + if(end < 0 || end > totalSize) + { + // overflow or greater than the total + end = totalSize; + pageSize = end - skipCount; + } + else + { + pageSize = maxItems; + } + if(pageSize < 0) + { + pageSize = 0; + } + boolean hasMoreItems = end < totalSize; + return new PageDetails(pageSize, hasMoreItems, skipCount, maxItems, end); + } +} diff --git a/src/main/java/org/alfresco/query/PagingRequest.java b/src/main/java/org/alfresco/query/PagingRequest.java new file mode 100644 index 0000000000..a35639e45f --- /dev/null +++ b/src/main/java/org/alfresco/query/PagingRequest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import org.alfresco.api.AlfrescoPublicApi; + +/** + * Simple wrapper for single page request (with optional request for total count up to a given max) + * + * @author janv + * @since 4.0 + */ +@AlfrescoPublicApi +public class PagingRequest +{ + private int skipCount = CannedQueryPageDetails.DEFAULT_SKIP_RESULTS; + private int maxItems; + + private int requestTotalCountMax = 0; // request total count up to a given max (0 => do not request total count) + private String queryExecutionId; + + /** + * Construct a page request + * + * @param maxItems the maximum number of items per page + */ + public PagingRequest(int maxItems) + { + this.maxItems = maxItems; + } + + /** + * Construct a page request + * + * @param maxItems the maximum number of items per page + * @param skipCount the number of items to skip before the first page + */ + public PagingRequest(int skipCount, int maxItems) + { + this.skipCount = skipCount; + this.maxItems = maxItems; + } + + /** + * Construct a page request + * + * @param maxItems the maximum number of items per page + * @param queryExecutionId a query execution ID associated with ealier paged requests + */ + public PagingRequest(int maxItems, String queryExecutionId) + { + setMaxItems(maxItems); + this.queryExecutionId = queryExecutionId; + } + + /** + * Construct a page request + * + * @param skipCount the number of items to skip before the first page + * @param maxItems the maximum number of items per page + * @param queryExecutionId a query execution ID associated with ealier paged requests + */ + public PagingRequest(int skipCount, int maxItems, String queryExecutionId) + { + setSkipCount(skipCount); + setMaxItems(maxItems); + this.queryExecutionId = queryExecutionId; + } + + /** + * Results to skip before retrieving the page. Usually a multiple of page size (ie. page size * num pages to skip). + * Default is 0. + * + * @return the number of results to skip before the page + */ + public int getSkipCount() + { + return skipCount; + } + + /** + * Change the skip count. Must be called before the paging query is run. + */ + protected void setSkipCount(int skipCount) + { + this.skipCount = (skipCount < 0 ? CannedQueryPageDetails.DEFAULT_SKIP_RESULTS : skipCount); + } + + /** + * Size of the page - if skip count is 0 then return up to max items. + * + * @return the maximum size of the page + */ + public int getMaxItems() + { + return maxItems; + } + + /** + * Change the size of the page. Must be called before the paging query is run. + */ + protected void setMaxItems(int maxItems) + { + this.maxItems = (maxItems < 0 ? CannedQueryPageDetails.DEFAULT_PAGE_SIZE : maxItems); + } + + /** + * Get requested total count (up to a given maximum). + */ + public int getRequestTotalCountMax() + { + return requestTotalCountMax; + } + + /** + * Set request total count (up to a given maximum). Default is 0 => do not request total count (which allows possible query optimisation). + * + * @param requestTotalCountMax + */ + public void setRequestTotalCountMax(int requestTotalCountMax) + { + this.requestTotalCountMax = requestTotalCountMax; + } + + /** + * Get a unique ID associated with these query results. This must be available before and + * after execution i.e. it must depend on the type of query and the query parameters + * rather than the execution results. Client has the option to pass this back as a hint when + * paging. + * + * @return a unique ID associated with the query execution results + */ + public String getQueryExecutionId() + { + return queryExecutionId; + } + + /** + * Change the unique query ID for the results. Must be called before the paging query is run. + */ + protected void setQueryExecutionId(String queryExecutionId) + { + this.queryExecutionId = queryExecutionId; + } +} diff --git a/src/main/java/org/alfresco/query/PagingResults.java b/src/main/java/org/alfresco/query/PagingResults.java new file mode 100644 index 0000000000..8cfb2e87b8 --- /dev/null +++ b/src/main/java/org/alfresco/query/PagingResults.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.List; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.util.Pair; + +/** + * Marker interface for single page of results + * + * @author janv + * @since 4.0 + */ +@AlfrescoPublicApi +public interface PagingResults +{ + /** + * Get the page of results. + * + * @return the results - possibly empty but never null + */ + public List getPage(); + + /** + * True if more items on next page. + *

+ * Note: could also return true if page was cutoff/trimmed for some reason + * (eg. due to permission checks of large page of requested max items) + * + * @return true if more items (eg. on next page)
+ * - true => at least one more page (or incomplete page - if cutoff)
+ * - false => last page (or incomplete page - if cutoff) + */ + public boolean hasMoreItems(); + + /** + * Get the total result count assuming no paging applied. This value will only be available if + * the query supports it and the client requested it. By default, it is not requested. + *

+ * Returns result as an approx "range" pair + *

    + *
  • null (or lower is null): unknown total count (or not requested by the client).
  • + *
  • lower = upper : total count should be accurate
  • + *
  • lower < upper : total count is an approximation ("about") - somewhere in the given range (inclusive)
  • + *
  • upper is null : total count is "more than" lower (upper is unknown)
  • + *
+ * + * @return Returns the total results as a range (all results, including the paged results returned) + */ + public Pair getTotalResultCount(); + + /** + * Get a unique ID associated with these query results. This must be available before and + * after execution i.e. it must depend on the type of query and the query parameters + * rather than the execution results. Client has the option to pass this back as a hint when + * paging. + * + * @return a unique ID associated with the query execution results + */ + public String getQueryExecutionId(); +} diff --git a/src/main/java/org/alfresco/query/PermissionedResults.java b/src/main/java/org/alfresco/query/PermissionedResults.java new file mode 100644 index 0000000000..b31613890c --- /dev/null +++ b/src/main/java/org/alfresco/query/PermissionedResults.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +/** + * Marker interface to show that permissions have already been applied to the results (and possibly cutoff) + * + * @author janv + * @since 4.0 + */ +public interface PermissionedResults +{ + /** + * @return true - if permissions have been applied to the results + */ + public boolean permissionsApplied(); + + /** + * @return true - if permission checks caused results to be cutoff (either due to max count or max time) + */ + public boolean hasMoreItems(); +} diff --git a/src/main/java/org/alfresco/scripts/ScriptException.java b/src/main/java/org/alfresco/scripts/ScriptException.java new file mode 100644 index 0000000000..6fb5b6ac04 --- /dev/null +++ b/src/main/java/org/alfresco/scripts/ScriptException.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.scripts; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * @author Kevin Roast + */ +public class ScriptException extends AlfrescoRuntimeException +{ + private static final long serialVersionUID = 1739480648583299623L; + + /** + * @param msgId String + */ + public ScriptException(String msgId) + { + super(msgId); + } + + /** + * @param msgId String + * @param cause Throwable + */ + public ScriptException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + /** + * @param msgId String + * @param params Object[] + */ + public ScriptException(String msgId, Object[] params) + { + super(msgId, params); + } + + /** + * @param msgId String + * @param msgParams Object[] + * @param cause Throwable + */ + public ScriptException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } +} diff --git a/src/main/java/org/alfresco/scripts/ScriptResourceHelper.java b/src/main/java/org/alfresco/scripts/ScriptResourceHelper.java new file mode 100644 index 0000000000..06be2ef1f0 --- /dev/null +++ b/src/main/java/org/alfresco/scripts/ScriptResourceHelper.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.scripts; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; + +/** + * @author Kevin Roast + */ +public class ScriptResourceHelper +{ + private static final String SCRIPT_ROOT = "_root"; + private static final String IMPORT_PREFIX = " + * Multiple includes of the same resource are dealt with correctly and nested includes of scripts + * is fully supported. + *

+ * Note that for performance reasons the script import directive syntax and placement in the file + * is very strict. The import lines must always be first in the file - even before any comments. + * Immediately that the script service detects a non-import line it will assume the rest of the + * file is executable script and no longer attempt to search for any further import directives. Therefore + * all imports should be at the top of the script, one following the other, in the correct syntax and + * with no comments present - the only separators valid between import directives is white space. + * + * @param script The script content to resolve imports in + * + * @return a valid script with all nested includes resolved into a single script instance + */ + public static String resolveScriptImports(String script, ScriptResourceLoader loader, Log logger) + { + // use a linked hashmap to preserve order of includes - the key in the collection is used + // to resolve multiple includes of the same scripts and therefore cyclic includes also + Map scriptlets = new LinkedHashMap(8, 1.0f); + + // perform a recursive resolve of all script imports + recurseScriptImports(SCRIPT_ROOT, script, loader, scriptlets, logger); + + if (scriptlets.size() == 1) + { + // quick exit for single script with no includes + if (logger.isTraceEnabled()) + logger.trace("Script content resolved to:\r\n" + script); + + return script; + } + else + { + // calculate total size of buffer required for the script and all includes + int length = 0; + for (String scriptlet : scriptlets.values()) + { + length += scriptlet.length(); + } + // append the scripts together to make a single script + StringBuilder result = new StringBuilder(length); + for (String scriptlet : scriptlets.values()) + { + result.append(scriptlet); + } + + if (logger.isTraceEnabled()) + logger.trace("Script content resolved to:\r\n" + result.toString()); + + return result.toString(); + } + } + + /** + * Recursively resolve imports in the specified scripts, adding the imports to the + * specific list of scriplets to combine later. + * + * @param location Script location - used to ensure duplicates are not added + * @param script The script to recursively resolve imports for + * @param scripts The collection of scriplets to execute with imports resolved and removed + */ + private static void recurseScriptImports( + String location, String script, ScriptResourceLoader loader, Map scripts, Log logger) + { + int index = 0; + // skip any initial whitespace + for (; index') + { + // found end of import line - so we have a resource path + String resource = script.substring(resourceStart, index); + + if (logger.isDebugEnabled()) + logger.debug("Found script resource import: " + resource); + + if (scripts.containsKey(resource) == false) + { + // load the script resource (and parse any recursive includes...) + String includedScript = loader.loadScriptResource(resource); + if (includedScript != null) + { + if (logger.isDebugEnabled()) + logger.debug("Succesfully located script '" + resource + "'"); + recurseScriptImports(resource, includedScript, loader, scripts, logger); + } + } + else + { + if (logger.isDebugEnabled()) + logger.debug("Note: already imported resource: " + resource); + } + + // continue scanning this script for additional includes + // skip the last two characters of the import directive + for (index += 2; index"); + } + else + { + throw new ScriptException( + "Malformed 'import' line - must be first in file, no comments and strictly of the form:" + + "\r\n"); + } + } + else + { + // no (further) includes found - include the original script content + if (logger.isDebugEnabled()) + logger.debug("Imports resolved, adding resource '" + location); + if (logger.isTraceEnabled()) + logger.trace(script); + scripts.put(location, script); + } + } +} diff --git a/src/main/java/org/alfresco/scripts/ScriptResourceLoader.java b/src/main/java/org/alfresco/scripts/ScriptResourceLoader.java new file mode 100644 index 0000000000..dfa4683794 --- /dev/null +++ b/src/main/java/org/alfresco/scripts/ScriptResourceLoader.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.scripts; + +/** + * @author Kevin Roast + */ +public interface ScriptResourceLoader +{ + public String loadScriptResource(String resource); +} diff --git a/src/main/java/org/alfresco/util/AbstractTriggerBean.java b/src/main/java/org/alfresco/util/AbstractTriggerBean.java new file mode 100644 index 0000000000..079374693e --- /dev/null +++ b/src/main/java/org/alfresco/util/AbstractTriggerBean.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.bean.BooleanBean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.Trigger; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.scheduling.quartz.JobDetailAwareTrigger; + +/** + * A utility bean to wrap sceduling a job with a scheduler. + * + * @author Andy Hind + */ +@AlfrescoPublicApi +public abstract class AbstractTriggerBean implements InitializingBean, JobDetailAwareTrigger, BeanNameAware, DisposableBean +{ + + protected static Log logger = LogFactory.getLog(AbstractTriggerBean.class); + + private JobDetail jobDetail; + + private Scheduler scheduler; + + private String beanName; + + private Trigger trigger; + + private boolean enabled = true; + + public AbstractTriggerBean() + { + super(); + } + + /** + * Get the definition of the job to run. + */ + public JobDetail getJobDetail() + { + return jobDetail; + } + + /** + * Set the definition of the job to run. + * + * @param jobDetail + */ + public void setJobDetail(JobDetail jobDetail) + { + this.jobDetail = jobDetail; + } + + /** + * Get the scheduler with which the job and trigger are scheduled. + * + * @return The scheduler + */ + public Scheduler getScheduler() + { + return scheduler; + } + + /** + * Set the scheduler. + * + * @param scheduler + */ + public void setScheduler(Scheduler scheduler) + { + this.scheduler = scheduler; + } + + /** + * Set the scheduler + */ + public void afterPropertiesSet() throws Exception + { + // Check properties are set + if (jobDetail == null) + { + throw new AlfrescoRuntimeException("Job detail has not been set"); + } + if (scheduler == null) + { + logger.warn("Job " + getBeanName() + " is not active"); + } + else if (!enabled) + { + logger.warn("Job " + getBeanName() + " is not enabled"); + } + else + { + logger.info("Job " + getBeanName() + " is active and enabled"); + // Register the job with the scheduler + this.trigger = getTrigger(); + if (this.trigger == null) + { + logger.error("Job " + getBeanName() + " is not active (invalid trigger)"); + } + else + { + logger.info("Job " + getBeanName() + " is active"); + + String jobName = jobDetail.getKey().getName(); + String groupName = jobDetail.getKey().getGroup(); + + if(scheduler.getJobDetail(jobName, groupName) != null) + { + // Job is already defined delete it + if(logger.isDebugEnabled()) + { + logger.debug("job already registered with scheduler jobName:" + jobName); + } + scheduler.deleteJob(jobName, groupName); + } + + if(logger.isDebugEnabled()) + { + logger.debug("schedule job:" + jobDetail + " using " + this.trigger + + " startTime: " + this.trigger.getStartTime()); + } + scheduler.scheduleJob(jobDetail, this.trigger); + } + } + } + + /** + * Ensures that the job is unscheduled with the context is shut down. + */ + public void destroy() throws Exception + { + if (this.trigger != null) + { + if (!this.scheduler.isShutdown()) + { + scheduler.unscheduleJob(this.trigger.getName(), this.trigger.getGroup()); + } + this.trigger = null; + } + } + + /** + * Abstract method for implementations to build their trigger. + * + * @return The trigger + * @throws Exception + */ + public abstract Trigger getTrigger() throws Exception; + + /** + * Get the bean name as this trigger is created + */ + public void setBeanName(String name) + { + this.beanName = name; + } + + /** + * Get the bean/trigger name. + * + * @return The name of the bean + */ + public String getBeanName() + { + return beanName; + } + + + public boolean isEnabled() + { + return enabled; + } + + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + + public void setEnabledFromBean(BooleanBean enabled) + { + this.enabled = enabled.isTrue(); + } +} diff --git a/src/main/java/org/alfresco/util/ArgumentHelper.java b/src/main/java/org/alfresco/util/ArgumentHelper.java new file mode 100644 index 0000000000..fff0df53ec --- /dev/null +++ b/src/main/java/org/alfresco/util/ArgumentHelper.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.HashMap; +import java.util.Map; + +/** + * Utility class to assist in extracting program arguments. + * + * @author Derek Hulley + * @since V2.1-A + */ +public class ArgumentHelper +{ + private String usage; + private Map args; + + public static Map ripArgs(String ... args) + { + Map argsMap = new HashMap(5); + for (String arg : args) + { + int index = arg.indexOf('='); + if (!arg.startsWith("--") || index < 0 || index == arg.length() - 1) + { + // Ignore it + continue; + } + String name = arg.substring(2, index); + String value = arg.substring(index + 1, arg.length()); + argsMap.put(name, value); + } + return argsMap; + } + + public ArgumentHelper(String usage, String[] args) + { + this.usage = usage; + this.args = ArgumentHelper.ripArgs(args); + } + + /** + * @throws IllegalArgumentException if the argument doesn't match the requirements. + */ + public String getStringValue(String arg, boolean mandatory, boolean nonEmpty) + { + String value = args.get(arg); + if (value == null && mandatory) + { + throw new IllegalArgumentException("Argument '" + arg + "' is required."); + } + else if (value != null && value.length() == 0 && nonEmpty) + { + throw new IllegalArgumentException("Argument '" + arg + "' may not be empty."); + } + return value; + } + + /** + * @return Returns the value assigned or the minimum value if the parameter was not present + * @throws IllegalArgumentException if the argument doesn't match the requirements. + */ + public int getIntegerValue(String arg, boolean mandatory, int minValue, int maxValue) + { + String valueStr = args.get(arg); + if (valueStr == null) + { + if (mandatory) + { + throw new IllegalArgumentException("Argument '" + arg + "' is required."); + } + else + { + return minValue; + } + } + // Now convert + try + { + int value = Integer.parseInt(valueStr); + if (value < minValue || value > maxValue) + { + throw new IllegalArgumentException("Argument '" + arg + "' must be in range " + minValue + " to " + maxValue + "."); + } + return value; + } + catch (NumberFormatException e) + { + throw new IllegalArgumentException("Argument '" + arg + "' must be a valid integer."); + } + } + + public void printUsage() + { + System.out.println(usage); + } +} diff --git a/src/main/java/org/alfresco/util/BridgeTable.java b/src/main/java/org/alfresco/util/BridgeTable.java new file mode 100644 index 0000000000..04b582293f --- /dev/null +++ b/src/main/java/org/alfresco/util/BridgeTable.java @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Generic bridge table support with optional reference counting to allow multiple membership for an object via several + * relationships. + * + * @author Andy + */ +public class BridgeTable +{ + HashMap>> descendants = new HashMap>>(); + + HashMap>> ancestors = new HashMap>>(); + + ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + public void addLink(T parent, T child) + { + readWriteLock.writeLock().lock(); + try + { + addDescendants(parent, child); + addAncestors(parent, child); + } + finally + { + readWriteLock.writeLock().unlock(); + } + + } + + public void addLink(Pair link) + { + addLink(link.getFirst(), link.getSecond()); + } + + public void addLinks(Collection> links) + { + for (Pair link : links) + { + addLink(link); + } + } + + public void removeLink(T parent, T child) + { + readWriteLock.writeLock().lock(); + try + { + removeDescendants(parent, child); + removeAncestors(parent, child); + } + finally + { + readWriteLock.writeLock().unlock(); + } + + } + + public void removeLink(Pair link) + { + removeLink(link.getFirst(), link.getSecond()); + } + + public void removeLinks(Collection> links) + { + for (Pair link : links) + { + removeLink(link); + } + } + + public HashSet getDescendants(T node) + { + return getDescendants(node, 1, Integer.MAX_VALUE); + } + + public HashSet getDescendants(T node, int position) + { + return getDescendants(node, position, position); + } + + public HashSet getDescendants(T node, int start, int end) + { + HashSet answer = new HashSet(); + HashMap> found = descendants.get(node); + if (found != null) + { + for (Integer key : found.keySet()) + { + if ((key.intValue() >= start) && (key.intValue() <= end)) + { + HashMap asd = found.get(key); + answer.addAll(asd.keySet()); + } + } + } + return answer; + } + + public HashSet getAncestors(T node) + { + return getAncestors(node, 1, Integer.MAX_VALUE); + } + + public HashSet getAncestors(T node, int position) + { + return getAncestors(node, position, position); + } + + public HashSet getAncestors(T node, int start, int end) + { + HashSet answer = new HashSet(); + HashMap> found = ancestors.get(node); + if (found != null) + { + for (Integer key : found.keySet()) + { + if ((key.intValue() >= start) && (key.intValue() <= end)) + { + HashMap asd = found.get(key); + answer.addAll(asd.keySet()); + } + } + } + return answer; + } + + /** + * @param parent T + * @param child T + */ + private void addDescendants(T parent, T child) + { + HashMap> parentsDescendants = descendants.get(parent); + if (parentsDescendants == null) + { + parentsDescendants = new HashMap>(); + descendants.put(parent, parentsDescendants); + } + + HashMap> childDescendantsToAdd = descendants.get(child); + + // add all the childs children to the parents descendants + + add(childDescendantsToAdd, Integer.valueOf(0), parentsDescendants, child); + + // add childs descendants to all parents ancestors at the correct depth + + HashMap> ancestorsToFixUp = ancestors.get(parent); + + if (ancestorsToFixUp != null) + { + for (Integer ancestorPosition : ancestorsToFixUp.keySet()) + { + HashMap ancestorsToFixUpAtPosition = ancestorsToFixUp.get(ancestorPosition); + for (T ancestorToFixUpAtPosition : ancestorsToFixUpAtPosition.keySet()) + { + HashMap> ancestorDescendants = descendants.get(ancestorToFixUpAtPosition); + add(childDescendantsToAdd, ancestorPosition, ancestorDescendants, child); + } + } + } + } + + /** + * @param parent T + * @param child T + */ + private void removeDescendants(T parent, T child) + { + HashMap> parentsDescendants = descendants.get(parent); + if (parentsDescendants == null) + { + return; + } + + HashMap> childDescendantsToRemove = descendants.get(child); + + // add all the childs children to the parents descendants + + remove(childDescendantsToRemove, Integer.valueOf(0), parentsDescendants, child); + + // add childs descendants to all parents ancestors at the correct depth + + HashMap> ancestorsToFixUp = ancestors.get(parent); + + if (ancestorsToFixUp != null) + { + for (Integer ancestorPosition : ancestorsToFixUp.keySet()) + { + HashMap ancestorsToFixUpAtPosition = ancestorsToFixUp.get(ancestorPosition); + for (T ancestorToFixUpAtPosition : ancestorsToFixUpAtPosition.keySet()) + { + HashMap> ancestorDescendants = descendants.get(ancestorToFixUpAtPosition); + remove(childDescendantsToRemove, ancestorPosition, ancestorDescendants, child); + } + } + } + } + + /** + * @param parent T + * @param child T + */ + private void removeAncestors(T parent, T child) + { + HashMap> childsAncestors = ancestors.get(child); + if (childsAncestors == null) + { + return; + } + + HashMap> parentAncestorsToRemove = ancestors.get(parent); + + // add all the childs children to the parents descendants + + remove(parentAncestorsToRemove, Integer.valueOf(0), childsAncestors, parent); + + // add childs descendants to all parents ancestors at the correct depth + + HashMap> decendantsToFixUp = descendants.get(child); + + if (decendantsToFixUp != null) + { + for (Integer descendantPosition : decendantsToFixUp.keySet()) + { + HashMap decendantsToFixUpAtPosition = decendantsToFixUp.get(descendantPosition); + for (T descendantToFixUpAtPosition : decendantsToFixUpAtPosition.keySet()) + { + HashMap> descendantAncestors = ancestors.get(descendantToFixUpAtPosition); + remove(parentAncestorsToRemove, descendantPosition, descendantAncestors, parent); + } + } + } + } + + /** + * @param toAdd HashMap> + * @param position Integer + * @param target HashMap> + * @param node T + */ + private void add(HashMap> toAdd, Integer position, HashMap> target, T node) + { + // add direct child + Integer directKey = Integer.valueOf(position.intValue() + 1); + HashMap direct = target.get(directKey); + if (direct == null) + { + direct = new HashMap(); + target.put(directKey, direct); + } + Counter counter = direct.get(node); + if (counter == null) + { + counter = new Counter(); + direct.put(node, counter); + } + counter.increment(); + + if (toAdd != null) + { + for (Integer depth : toAdd.keySet()) + { + Integer newKey = Integer.valueOf(position.intValue() + depth.intValue() + 1); + HashMap toAddAtDepth = toAdd.get(depth); + HashMap targetAtDepthPlusOne = target.get(newKey); + if (targetAtDepthPlusOne == null) + { + targetAtDepthPlusOne = new HashMap(); + target.put(newKey, targetAtDepthPlusOne); + } + + for (T key : toAddAtDepth.keySet()) + { + Counter counterToAdd = toAddAtDepth.get(key); + Counter counterToAddTo = targetAtDepthPlusOne.get(key); + if (counterToAddTo == null) + { + counterToAddTo = new Counter(); + targetAtDepthPlusOne.put(key, counterToAddTo); + } + counterToAddTo.add(counterToAdd); + } + } + } + } + + /** + * @param toRemove HashMap> + * @param position Integer + * @param target HashMap> + * @param node T + */ + private void remove(HashMap> toRemove, Integer position, HashMap> target, T node) + { + // remove direct child + Integer directKey = Integer.valueOf(position.intValue() + 1); + HashMap direct = target.get(directKey); + if (direct != null) + { + Counter counter = direct.get(node); + if (counter != null) + { + counter.decrement(); + if (counter.getCount() == 0) + { + direct.remove(node); + } + } + + } + + if (toRemove != null) + { + for (Integer depth : toRemove.keySet()) + { + Integer newKey = Integer.valueOf(position.intValue() + depth.intValue() + 1); + HashMap toRemoveAtDepth = toRemove.get(depth); + HashMap targetAtDepthPlusOne = target.get(newKey); + if (targetAtDepthPlusOne != null) + { + for (T key : toRemoveAtDepth.keySet()) + { + Counter counterToRemove = toRemoveAtDepth.get(key); + Counter counterToRemoveFrom = targetAtDepthPlusOne.get(key); + if (counterToRemoveFrom != null) + { + counterToRemoveFrom.remove(counterToRemove); + if (counterToRemoveFrom.getCount() == 0) + { + targetAtDepthPlusOne.remove(key); + } + } + } + } + } + } + } + + /** + * @param parent T + * @param child T + */ + private void addAncestors(T parent, T child) + { + HashMap> childsAncestors = ancestors.get(child); + if (childsAncestors == null) + { + childsAncestors = new HashMap>(); + ancestors.put(child, childsAncestors); + } + + HashMap> parentAncestorsToAdd = ancestors.get(parent); + + // add all the childs children to the parents descendants + + add(parentAncestorsToAdd, Integer.valueOf(0), childsAncestors, parent); + + // add childs descendants to all parents ancestors at the correct depth + + HashMap> descenantsToFixUp = descendants.get(child); + + if (descenantsToFixUp != null) + { + for (Integer descendantPosition : descenantsToFixUp.keySet()) + { + HashMap descenantsToFixUpAtPosition = descenantsToFixUp.get(descendantPosition); + for (T descenantToFixUpAtPosition : descenantsToFixUpAtPosition.keySet()) + { + HashMap> descendatAncestors = ancestors.get(descenantToFixUpAtPosition); + add(parentAncestorsToAdd, descendantPosition, descendatAncestors, parent); + } + } + } + } + + public int size() + { + return ancestors.size(); + } + + private static class Counter + { + int count = 0; + + void increment() + { + count++; + } + + void decrement() + { + count--; + } + + int getCount() + { + return count; + } + + void add(Counter other) + { + count += other.count; + } + + void remove(Counter other) + { + count -= other.count; + } + } + + /** + * @return Set + */ + public Set keySet() + { + return ancestors.keySet(); + } +} diff --git a/src/main/java/org/alfresco/util/CachingDateFormat.java b/src/main/java/org/alfresco/util/CachingDateFormat.java new file mode 100644 index 0000000000..263c20c045 --- /dev/null +++ b/src/main/java/org/alfresco/util/CachingDateFormat.java @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.WeakHashMap; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.ISODateTimeFormat; +import org.springframework.extensions.surf.exception.PlatformRuntimeException; + +/** + * Provides thread safe means of obtaining a cached date formatter. + *

+ * The cached string-date mappings are stored in a WeakHashMap. + * + * @see java.text.DateFormat#setLenient(boolean) + * + * @author Derek Hulley + */ +public class CachingDateFormat extends SimpleDateFormat +{ + private static final long serialVersionUID = 3258415049197565235L; + + /**

 yyyy-MM-dd'T'HH:mm:ss 
*/ + public static final String FORMAT_FULL_GENERIC = "yyyy-MM-dd'T'HH:mm:ss"; + + /**
 yyyy-MM-dd'T'HH:mm:ss 
*/ + public static final String FORMAT_CMIS_SQL = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + + public static final String FORMAT_SOLR = "yyyy-MM-dd'T'HH:mm:ss.SSSX"; + + public static final StringAndResolution[] LENIENT_FORMATS; + + + static + { + ArrayList list = new ArrayList (); + list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Calendar.MILLISECOND)); + list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss.SSS", Calendar.MILLISECOND)); + list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ssZ", Calendar.SECOND)); + list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss", Calendar.SECOND)); + list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mmZ", Calendar.MINUTE)); + list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm", Calendar.MINUTE)); + list.add( new StringAndResolution("yyyy-MM-dd'T'HHZ", Calendar.HOUR_OF_DAY)); + list.add( new StringAndResolution("yyyy-MM-dd'T'HH", Calendar.HOUR_OF_DAY)); + list.add( new StringAndResolution("yyyy-MM-dd'T'Z", Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution("yyyy-MM-dd'T'", Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution("yyyy-MM-ddZ", Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution("yyyy-MM-dd", Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution("yyyy-MMZ", Calendar.MONTH)); + list.add( new StringAndResolution("yyyy-MM", Calendar.MONTH)); + // year would duplicate :-) and eat stuff + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss.SSSZ", Calendar.MILLISECOND)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss.SSS", Calendar.MILLISECOND)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ssZ", Calendar.SECOND)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss", Calendar.SECOND)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mmZ", Calendar.MINUTE)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm", Calendar.MINUTE)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HHZ", Calendar.HOUR_OF_DAY)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH", Calendar.HOUR_OF_DAY)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'Z",Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution( "yyyy-MMM-dd'T'",Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution( "yyyy-MMM-ddZ", Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution( "yyyy-MMM-dd", Calendar.DAY_OF_MONTH)); + list.add( new StringAndResolution( "yyyy-MMMZ", Calendar.MONTH)); + list.add( new StringAndResolution( "yyyy-MMM", Calendar.MONTH)); + list.add( new StringAndResolution("yyyyZ", Calendar.YEAR)); + list.add( new StringAndResolution("yyyy", Calendar.YEAR)); + + + + LENIENT_FORMATS = list.toArray(new StringAndResolution[]{}); + } + + /**
 yyyy-MM-dd 
*/ + public static final String FORMAT_DATE_GENERIC = "yyyy-MM-dd"; + + /**
 HH:mm:ss 
*/ + public static final String FORMAT_TIME_GENERIC = "HH:mm:ss"; + + private static ThreadLocal s_localDateFormat = new ThreadLocal(); + + private static ThreadLocal s_localDateOnlyFormat = new ThreadLocal(); + + private static ThreadLocal s_localTimeOnlyFormat = new ThreadLocal(); + + private static ThreadLocal s_localCmisSqlDatetime = new ThreadLocal(); + + private static ThreadLocal s_localSolrDatetime = new ThreadLocal(); + + private static ThreadLocal s_lenientParsers = new ThreadLocal(); + + transient private Map cacheDates = new WeakHashMap(89); + + private CachingDateFormat(String format) + { + super(format); + } + + public String toString() + { + return this.toPattern(); + } + + /** + * @param length + * the type of date format, e.g. {@link CachingDateFormat#LONG } + * @param locale + * the Locale that will be used to determine the + * date pattern + * + * @see #getDateFormat(String, boolean) + * @see CachingDateFormat#SHORT + * @see CachingDateFormat#MEDIUM + * @see CachingDateFormat#LONG + * @see CachingDateFormat#FULL + */ + public static SimpleDateFormat getDateFormat(int length, Locale locale, boolean lenient) + { + SimpleDateFormat dateFormat = (SimpleDateFormat) CachingDateFormat.getDateInstance(length, locale); + // extract the format string + String pattern = dateFormat.toPattern(); + // we have a pattern to use + return getDateFormat(pattern, lenient); + } + + /** + * @param dateLength + * the type of date format, e.g. {@link CachingDateFormat#LONG } + * @param timeLength + * the type of time format, e.g. {@link CachingDateFormat#LONG } + * @param locale + * the Locale that will be used to determine the + * date pattern + * + * @see #getDateFormat(String, boolean) + * @see CachingDateFormat#SHORT + * @see CachingDateFormat#MEDIUM + * @see CachingDateFormat#LONG + * @see CachingDateFormat#FULL + */ + public static SimpleDateFormat getDateTimeFormat(int dateLength, int timeLength, Locale locale, boolean lenient) + { + SimpleDateFormat dateFormat = (SimpleDateFormat) CachingDateFormat.getDateTimeInstance(dateLength, timeLength, locale); + // extract the format string + String pattern = dateFormat.toPattern(); + // we have a pattern to use + return getDateFormat(pattern, lenient); + } + + /** + * @param pattern + * the conversion pattern to use + * @param lenient + * true to allow the parser to extract the date in conceivable + * manner + * @return Returns a conversion-cacheing formatter for the given pattern, + * but the instance itself is not cached + */ + public static SimpleDateFormat getDateFormat(String pattern, boolean lenient) + { + // create an alfrescoDateFormat for cacheing purposes + SimpleDateFormat dateFormat = new CachingDateFormat(pattern); + // set leniency + dateFormat.setLenient(lenient); + // done + return dateFormat; + } + + /** + * @return Returns a thread-safe formatter for the generic date/time format + * + * @see #FORMAT_FULL_GENERIC + */ + public static SimpleDateFormat getDateFormat() + { + if (s_localDateFormat.get() != null) + { + return s_localDateFormat.get(); + } + + CachingDateFormat formatter = new CachingDateFormat(FORMAT_FULL_GENERIC); + // it must be strict + formatter.setLenient(false); + // put this into the threadlocal object + s_localDateFormat.set(formatter); + // done + return s_localDateFormat.get(); + } + + /** + * @return Returns a thread-safe formatter for the cmis sql datetime format + */ + public static SimpleDateFormat getCmisSqlDatetimeFormat() + { + if (s_localCmisSqlDatetime.get() != null) + { + return s_localCmisSqlDatetime.get(); + } + + CachingDateFormat formatter = new CachingDateFormat(FORMAT_CMIS_SQL); + // it must be strict + formatter.setLenient(false); + // put this into the threadlocal object + s_localCmisSqlDatetime.set(formatter); + // done + return s_localCmisSqlDatetime.get(); + } + + /** + * @return Returns a thread-safe formatter for the cmis sql datetime format + */ + public static SimpleDateFormat getSolrDatetimeFormat() + { + if (s_localSolrDatetime.get() != null) + { + return s_localSolrDatetime.get(); + } + + CachingDateFormat formatter = new CachingDateFormat(FORMAT_SOLR); + // it must be strict + formatter.setLenient(false); + // put this into the threadlocal object + s_localSolrDatetime.set(formatter); + // done + return s_localSolrDatetime.get(); + } + + /** + * @return Returns a thread-safe formatter for the generic date format + * + * @see #FORMAT_DATE_GENERIC + */ + public static SimpleDateFormat getDateOnlyFormat() + { + if (s_localDateOnlyFormat.get() != null) + { + return s_localDateOnlyFormat.get(); + } + + CachingDateFormat formatter = new CachingDateFormat(FORMAT_DATE_GENERIC); + // it must be strict + formatter.setLenient(false); + // put this into the threadlocal object + s_localDateOnlyFormat.set(formatter); + // done + return s_localDateOnlyFormat.get(); + } + + /** + * @return Returns a thread-safe formatter for the generic time format + * + * @see #FORMAT_TIME_GENERIC + */ + public static SimpleDateFormat getTimeOnlyFormat() + { + if (s_localTimeOnlyFormat.get() != null) + { + return s_localTimeOnlyFormat.get(); + } + + CachingDateFormat formatter = new CachingDateFormat(FORMAT_TIME_GENERIC); + // it must be strict + formatter.setLenient(false); + // put this into the threadlocal object + s_localTimeOnlyFormat.set(formatter); + // done + return s_localTimeOnlyFormat.get(); + } + + /** + * Parses and caches date strings. + * + * @see java.text.DateFormat#parse(java.lang.String, + * java.text.ParsePosition) + */ + public Date parse(String text, ParsePosition pos) + { + Date cached = cacheDates.get(text); + if (cached == null) + { + Date date = super.parse(text, pos); + if ((date != null) && (pos.getIndex() == text.length())) + { + cacheDates.put(text, date); + Date clonedDate = (Date) date.clone(); + return clonedDate; + } + else + { + return date; + } + } + else + { + pos.setIndex(text.length()); + Date clonedDate = (Date) cached.clone(); + return clonedDate; + } + } + + public static Pair lenientParse(String text, int minimumResolution) throws ParseException + { + DateTimeFormatter fmt = ISODateTimeFormat.dateTime(); + try + { + Date parsed = fmt.parseDateTime(text).toDate(); + return new Pair(parsed, Calendar.MILLISECOND); + } + catch(IllegalArgumentException e) + { + + } + + SimpleDateFormatAndResolution[] formatters = getLenientFormatters(); + for(SimpleDateFormatAndResolution formatter : formatters) + { + if(formatter.resolution >= minimumResolution) + { + ParsePosition pp = new ParsePosition(0); + Date parsed = formatter.simpleDateFormat.parse(text, pp); + if ((pp.getIndex() < text.length()) || (parsed == null)) + { + continue; + } + return new Pair(parsed, formatter.resolution); + } + } + + throw new ParseException("Unknown date format", 0); + + + } + + public static SimpleDateFormatAndResolution[] getLenientFormatters() + { + if (s_lenientParsers.get() != null) + { + return s_lenientParsers.get(); + } + + int i = 0; + SimpleDateFormatAndResolution[] formatters = new SimpleDateFormatAndResolution[LENIENT_FORMATS.length]; + for(StringAndResolution format : LENIENT_FORMATS) + { + CachingDateFormat formatter = new CachingDateFormat(format.string); + // it must be strict + formatter.setLenient(false); + formatters[i++] = new SimpleDateFormatAndResolution(formatter, format.resolution); + } + + // put this into the threadlocal object + s_lenientParsers.set(formatters); + // done + return s_lenientParsers.get(); + } + + public static class StringAndResolution + { + String string; + int resolution; + + /** + * @return the resolution + */ + public int getResolution() + { + return resolution; + } + + /** + * @param resolution the resolution to set + */ + public void setResolution(int resolution) + { + this.resolution = resolution; + } + + StringAndResolution(String string, int resolution) + { + this.string = string; + this.resolution = resolution; + } + } + + public static class SimpleDateFormatAndResolution + { + SimpleDateFormat simpleDateFormat; + int resolution; + + SimpleDateFormatAndResolution(SimpleDateFormat simpleDateFormat, int resolution) + { + this.simpleDateFormat = simpleDateFormat; + this.resolution = resolution; + } + + /** + * @return the simpleDateFormat + */ + public SimpleDateFormat getSimpleDateFormat() + { + return simpleDateFormat; + } + + /** + * @return the resolution + */ + public int getResolution() + { + return resolution; + } + + } +} diff --git a/src/main/java/org/alfresco/util/Content.java b/src/main/java/org/alfresco/util/Content.java new file mode 100644 index 0000000000..d507d9c747 --- /dev/null +++ b/src/main/java/org/alfresco/util/Content.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + + +/** + * Content + * + * @author dcaruana + */ +public interface Content +{ + /** + * Gets content as a string + * + * @return content as a string + * @throws IOException + */ + public String getContent() throws IOException; + + /** + * Gets the content mimetype + * + * @return mimetype + */ + public String getMimetype(); + + /** + * Gets the content encoding + * + * @return encoding + */ + public String getEncoding(); + + /** + * Gets the content length (in bytes) + * + * @return length + */ + public long getSize(); + + /** + * Gets the content input stream + * + * @return input stream + */ + public InputStream getInputStream(); + + /** + * Gets the content reader (which is sensitive to encoding) + * + * @return Reader + */ + public Reader getReader() throws IOException; +} diff --git a/src/main/java/org/alfresco/util/Convert.java b/src/main/java/org/alfresco/util/Convert.java new file mode 100644 index 0000000000..5074ff9691 --- /dev/null +++ b/src/main/java/org/alfresco/util/Convert.java @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.alfresco.encoding.CharactersetFinder; +import org.alfresco.encoding.GuessEncodingCharsetFinder; +import org.alfresco.util.exec.RuntimeExec; +import org.alfresco.util.exec.RuntimeExec.ExecutionResult; + +/** + * Utility to convert text files. + *

+ * Check the usage options with the --help option. + *

+ * Here are some examples of how to use the main method: + *

    + *
  • + * --help
    + * Produce the help output. + *
  • + *
  • + * --dry-run --encoding=UTF-8 --line-ending=WINDOWS --match="(.java|.xml|.jsp|.properties)$" --ignore="(.svn|classes)" "w:\"
    + * Find all source (.java, .xml, .jsp and .properties) files in directory "w:\".
    + * List files and show which would change when converting to CR-LF (Windows) line endings.
    + * Where auto-detection of the file is ambiguous, assume UTF-8. + *
  • + *
  • + * --encoding=UTF-8 --line-ending=WINDOWS --match="(.java|.xml|.jsp|.properties)$" --ignore="(.svn|classes)" "w:\"
    + * Find all source (.java, .xml, .jsp and .properties) files in directory "w:\". Recurse into subdirectories.
    + * Convert files, where necessary, to have CR-LF (Windows) line endings.
    + * Where auto-detection of the file encoding is ambiguous, assume UTF-8.
    + * Backups (.bak) files will be created. + *
  • + *
  • + * --svn-update --no-backup --encoding=UTF-8 --line-ending=WINDOWS --match="(.java|.xml|.jsp|.properties)$" "w:\"
    + * Issue a 'svn status' command on directory "w:\" and match the regular expressions given to find files.
    + * Convert files, where necessary, to have CR-LF (Windows) line endings.
    + * Where auto-detection of the file encoding is ambiguous, assume UTF-8. Write out as UTF-8.
    + * No backups files will be created. + *
  • + *
+ * + * @author Derek Hulley + */ +public class Convert +{ + private static final String OPTION_HELP = "--help"; + private static final String OPTION_SVN_STATUS = "--svn-status"; + private static final String OPTION_MATCH = "--match="; + private static final String OPTION_IGNORE = "--ignore="; + private static final String OPTION_ENCODING= "--encoding="; + private static final String OPTION_LINE_ENDING = "--line-ending="; + private static final String OPTION_REPLACE_TABS= "--replace-tabs="; + private static final String OPTION_NO_RECURSE = "--no-recurse"; + private static final String OPTION_NO_BACKUP = "--no-backup"; + private static final String OPTION_DRY_RUN = "--dry-run"; + private static final String OPTION_VERBOSE = "--verbose"; + private static final String OPTION_QUIET = "--quiet"; + + private static final Set OPTIONS = new HashSet(13); + + static + { + OPTIONS.add(OPTION_HELP); + OPTIONS.add(OPTION_SVN_STATUS); + OPTIONS.add(OPTION_MATCH); + OPTIONS.add(OPTION_IGNORE); + OPTIONS.add(OPTION_ENCODING); + OPTIONS.add(OPTION_LINE_ENDING); + OPTIONS.add(OPTION_REPLACE_TABS); + OPTIONS.add(OPTION_NO_RECURSE); + OPTIONS.add(OPTION_NO_BACKUP); + OPTIONS.add(OPTION_DRY_RUN); + OPTIONS.add(OPTION_VERBOSE); + OPTIONS.add(OPTION_QUIET); + } + + /** + * @see GuessEncodingCharsetFinder + */ + private static final CharactersetFinder CHARACTER_ENCODING_FINDER = new GuessEncodingCharsetFinder(); + + private File startDir = null; + + private boolean svnStatus = false; + private boolean dryRun = false; + private Pattern matchPattern = null; + private Pattern ignorePattern = null; + private Charset charset = null; + private String lineEnding = null; + private Integer replaceTabs = null; + private boolean noRecurse = false; + private boolean noBackup = false; + private boolean verbose = false; + private boolean quiet = false; + + public static void main(String[] args) + { + if (args.length < 1) + { + printUsage(); + } + // Convert args to a list + List argList = new ArrayList(args.length); + List argListFixed = Arrays.asList(args); + argList.addAll(argListFixed); + // Extract all the options + Map optionValues = extractOptions(argList); + + // Check for help request + if (optionValues.containsKey(OPTION_HELP)) + { + printUsage(); + System.exit(0); + } + + // Check + if (argList.size() != 1) + { + printUsage(); + System.exit(1); + } + + // Get the directory to start in + File startDir = new File(argList.get(0)); + if (!startDir.exists() || !startDir.isDirectory()) + { + System.err.println("Convert: "); + System.err.println(" Unable to find directory: " + startDir); + System.err.flush(); + printUsage(); + System.exit(1); + } + + Convert convert = new Convert(optionValues, startDir); + convert.convert(); + } + + /** + * Private constructor for use by the main method. + */ + private Convert(Map optionValues, File startDir) + { + this.startDir = startDir; + + svnStatus = optionValues.containsKey(OPTION_SVN_STATUS); + dryRun = optionValues.containsKey(OPTION_DRY_RUN); + String match = optionValues.get(OPTION_MATCH); + String ignore = optionValues.get(OPTION_IGNORE); + String encoding = optionValues.get(OPTION_ENCODING); + lineEnding = optionValues.get(OPTION_LINE_ENDING); + noRecurse = optionValues.containsKey(OPTION_NO_RECURSE); + noBackup = optionValues.containsKey(OPTION_NO_BACKUP); + verbose = optionValues.containsKey(OPTION_VERBOSE); + quiet = optionValues.containsKey(OPTION_QUIET); + + // Check that the tab replacement count is correct + String replaceTabsStr = optionValues.get(OPTION_REPLACE_TABS); + if (replaceTabsStr != null) + { + try + { + replaceTabs = Integer.parseInt(replaceTabsStr); + } + catch (NumberFormatException e) + { + System.err.println("Convert: "); + System.err.println(" Unable to determine how many spaces to replace tabs with: " + replaceTabsStr); + System.err.flush(); + printUsage(); + System.exit(1); + } + } + + // Check the match regex expressions + if (match == null) + { + match = ".*"; + } + try + { + matchPattern = Pattern.compile(match); + } + catch (Throwable e) + { + System.err.println("Convert: "); + System.err.println(" Unable to parse regular expression: " + match); + System.err.flush(); + printUsage(); + System.exit(1); + } + // Check the match regex expressions + if (ignore != null) + { + try + { + ignorePattern = Pattern.compile(ignore); + } + catch (Throwable e) + { + System.err.println("Convert: "); + System.err.println(" Unable to parse regular expression: " + ignore); + System.err.flush(); + printUsage(); + System.exit(1); + } + } + // Check the encoding + if (encoding != null) + { + try + { + charset = Charset.forName(encoding); + } + catch (Throwable e) + { + System.err.println("Convert: "); + System.err.println(" Unknown encoding: " + encoding); + System.err.flush(); + printUsage(); + System.exit(1); + } + } + + // Check line ending + if (lineEnding != null && !lineEnding.equals("WINDOWS") && !lineEnding.equals("UNIX")) + { + System.err.println("Convert: "); + System.err.println(" Line endings can be either WINDOWS or UNIX: " + lineEnding); + System.err.flush(); + printUsage(); + System.exit(1); + } + + // Check quiet/verbose match + if (verbose && quiet) + { + System.err.println("Convert: "); + System.err.println(" Cannot output in verbose and quiet mode."); + System.err.flush(); + printUsage(); + System.exit(1); + } + } + + private void convert() + { + try + { + if (!quiet) + { + System.out.print("Converting files matching " + matchPattern); + System.out.print(ignorePattern == null ? "" : " but not " + ignorePattern); + System.out.println(dryRun ? " [DRY RUN]" : ""); + } + if (!svnStatus) + { + // Do a recursive pattern match + convertDir(startDir); + } + else + { + // Use SVN + convertSvn(startDir); + } + } + catch (Throwable e) + { + e.printStackTrace(); + System.err.flush(); + printUsage(); + System.exit(1); + } + finally + { + System.out.flush(); + } + } + + private void convertSvn(File currentDir) throws Throwable + { + RuntimeExec exec = new RuntimeExec(); + exec.setCommand(new String[] {"svn", "status", currentDir.toString()}); + ExecutionResult result = exec.execute(); + if (!result.getSuccess()) + { + System.out.println("svn status command failed:" + exec); + } + // Get the output + String dump = result.getStdOut(); + BufferedReader reader = null; + try + { + reader = new BufferedReader(new StringReader(dump)); + while (true) + { + String line = reader.readLine(); + if (line == null) + { + break; + } + // Only lines that start with "A" or "M" + if (!line.startsWith("A") && !line.startsWith("M")) + { + continue; + } + String filename = line.substring(7).trim(); + if (filename.length() < 1) + { + continue; + } + File file = new File(filename); + if (!file.exists()) + { + continue; + } + // We found one + convertFile(file); + } + } + finally + { + if (reader != null) + { + try { reader.close(); } catch (Throwable e) {} + } + } + } + + /** + * Recursive method to do the conversion work. + */ + private void convertDir(File currentDir) throws Throwable + { + // Get all children of the folder + File[] childFiles = currentDir.listFiles(); + for (File childFile : childFiles) + { + if (childFile.isDirectory()) + { + if (noRecurse) + { + // Don't enter the directory + continue; + } + // Recurse + convertDir(childFile); + } + else + { + convertFile(childFile); + } + } + } + + private void convertFile(File file) throws Throwable + { + // We have a file, but does the pattern match + String filePath = file.getAbsolutePath(); + if (matchPattern.matcher(filePath).find()) + { + // It matches, but must we ignore it? + if (ignorePattern != null && ignorePattern.matcher(filePath).find()) + { + // It is ignorable + return; + } + } + else + { + // It missed the primary positive match + return; + } + + // Ignore folders + if (file.isDirectory()) + { + return; + } + + if (file.length() > (1024 * 1024)) // 1MB. TODO: Make an option + { + System.out.println(" (Too big)"); + } + File backupFile = null; + try + { + // Read the source file into memory + byte[] fileBytes = readFileIntoMemory(file); + // Calculate the MD5 for the file + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(fileBytes); + byte[] fileMd5 = md5.digest(); + // Guess the charset now + Charset fileCharset = guessCharset(fileBytes, charset); + + byte[] convertedBytes = fileBytes; + byte[] sourceBytes = fileBytes; + byte[] convertedMd5 = fileMd5; + + // Convert the tabs + if (replaceTabs != null) + { + sourceBytes = convertTabs(sourceBytes, fileCharset, replaceTabs); + } + // Convert the charset + if (charset != null) + { + // TODO + // sourceBytes = convert ... + } + // Convert the line endings + if (lineEnding != null) + { + convertedBytes = convertLineEndings(sourceBytes, fileCharset, lineEnding); + } + boolean changed = false; + if (convertedBytes == fileBytes) + { + // Nothing done + } + else + { + // Recalculate the converted MD5 + md5 = MessageDigest.getInstance("MD5"); + md5.update(convertedBytes); + convertedMd5 = md5.digest(); + // Now compare + changed = !Arrays.equals(fileMd5, convertedMd5); + } + // Make a backup of the file if it changed + if (changed) + { + if (!noBackup && !dryRun) + { + String backupFilename = file.getAbsolutePath() + ".bak"; + File backupFilePre = new File(backupFilename); + // Write the original file contents to the backup file + writeMemoryIntoFile(fileBytes, backupFilePre); + // That being successful, we can now reference it + backupFile = backupFilePre; + } + if (!quiet) + { + System.out.println(" " + file + " "); + } + // Only write to the file if this is not a dry run + if (!dryRun) + { + // Now write the converted buffer to the original file + writeMemoryIntoFile(convertedBytes, file); + } + } + else + { + if (verbose) + { + System.out.println(" " + file + " "); + } + } + } + catch (Throwable e) + { + if (backupFile != null) + { + try + { + file.delete(); + backupFile.renameTo(file); + } + catch (Throwable ee) + { + System.err.println("Failed to restore backup file: " + backupFile); + ee.printStackTrace(); + } + } + throw e; + } + finally + { + if (!quiet || verbose) + { + System.out.flush(); + } + } + } + + /** + * Brute force guessing by doing charset conversions.
+ */ + private static Charset guessCharset(byte[] bytes, Charset charset) throws Exception + { + Charset guessedCharset = CHARACTER_ENCODING_FINDER.detectCharset(bytes); + if (guessedCharset == null) + { + return charset; + } + else + { + return guessedCharset; + } + } + + private static byte[] convertTabs(byte[] bytes, Charset charset, int replaceTabs) throws Exception + { + // The tab character + char tab = '\t'; + char space = ' '; + + // The output + StringBuilder sb = new StringBuilder(bytes.length); + + String charsetName = charset.name(); + // Using the charset, convert to a string + String str = new String(bytes, charsetName); + char[] chars = str.toCharArray(); + for (char c : chars) + { + if (c == tab) + { + // Replace the tab + for (int i = 0; i < replaceTabs; i++) + { + sb.append(space); + } + } + else + { + sb.append(c); + } + } + // Done + return sb.toString().getBytes(charsetName); + } + + private static final String EOF_CHECK = "--EOF-CHECK--"; + private static byte[] convertLineEndings(byte[] bytes, Charset charset, String lineEnding) throws Exception + { + String charsetName = charset.name(); + // Using the charset, convert to a string + BufferedReader reader = null; + StringBuilder sb = new StringBuilder(bytes.length); + try + { + String str = new String(bytes, charsetName); + str = str + EOF_CHECK; + reader = new BufferedReader(new StringReader(str)); + String line = reader.readLine(); + while (line != null) + { + // Ignore the newline check + boolean addLine = true; + if (line.equals(EOF_CHECK)) + { + break; + } + else if (line.endsWith(EOF_CHECK)) + { + int index = line.indexOf(EOF_CHECK); + line = line.substring(0, index); + addLine = false; + } + // Write the line back out + sb.append(line); + if (!addLine) + { + // No newline + } + else if (lineEnding.equalsIgnoreCase("UNIX")) + { + sb.append("\n"); + } + else + { + sb.append("\r\n"); + } + line = reader.readLine(); + } + } + finally + { + if (reader != null) + { + try { reader.close(); } catch (Throwable e) {} + } + } + // Done + return sb.toString().getBytes(charsetName); + } + + private static byte[] readFileIntoMemory(File file) throws Exception + { + InputStream is = null; + OutputStream os = null; + try + { + is = new BufferedInputStream(new FileInputStream(file)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); + os = new BufferedOutputStream(baos); + byte[] buffer = new byte[1024]; + while (true) + { + int count = is.read(buffer); + if (count < 0) + { + break; + } + os.write(buffer, 0, count); + } + os.flush(); + byte[] memory = baos.toByteArray(); + return memory; + } + finally + { + if (is != null) + { + try { is.close(); } catch (Throwable e) {} + } + if (os != null) + { + try { os.close(); } catch (Throwable e) {} + } + } + } + + private static void writeMemoryIntoFile(byte[] bytes, File file) throws Exception + { + InputStream is = null; + OutputStream os = null; + try + { + is = new ByteArrayInputStream(bytes); + os = new BufferedOutputStream(new FileOutputStream(file)); + byte[] buffer = new byte[1024]; + while (true) + { + int count = is.read(buffer); + if (count < 0) + { + break; + } + os.write(buffer, 0, count); + } + os.flush(); + } + finally + { + if (is != null) + { + try { is.close(); } catch (Throwable e) {} + } + if (os != null) + { + try { os.close(); } catch (Throwable e) {} + } + } + } + + /** + * Extract all the options from the list of arguments. + * @param args the program arguments. This list will be modified. + * @return Returns a map of arguments and their values. Where the arguments have + * no values, an empty string is returned. + */ + private static Map extractOptions(List args) + { + Map optionValues = new HashMap(13); + // Iterate until we find a non-option + Iterator iterator = args.iterator(); + while (iterator.hasNext()) + { + String arg = iterator.next(); + boolean foundOption = false; + for (String option : OPTIONS) + { + if (!arg.startsWith(option)) + { + // It is a non-option + continue; + } + foundOption = true; + // We can remove the argument + iterator.remove(); + // Check if the option needs a value + if (option.endsWith("=")) + { + // Extract the option value + int index = arg.indexOf("="); + if (index == arg.length() - 1) + { + // There is nothing there, so we don't keep a value + } + else + { + String value = arg.substring(index + 1); + optionValues.put(option, value); + } + } + else + { + // Add the value to the map + String value = ""; + optionValues.put(option, value); + } + } + if (!foundOption) + { + // It is not an option + break; + } + } + // Done + return optionValues; + } + + public static void printUsage() + { + StringBuilder sb = new StringBuilder(1024); + sb.append("Usage: \n") + .append(" Convert [options] directory \n") + .append(" \n") + .append(" options: \n") + .append(" --help \n") + .append(" Print this help. \n") + .append(" --svn-status \n") + .append(" Execute a 'svn status' command against the directory and use the output for the file list. \n") + .append(" --match=?: \n") + .append(" A regular expression that all filenames must match. \n") + .append(" This argument can be escaped with double quotes, ie.g \"[a-zA-z0-9 ]\". \n") + .append(" The regular expression will be applied to the full path of the file. \n") + .append(" Name seperators will be '/' on Unix and ''\\'' on Windows systems. \n") + .append(" The default is \"--match=.*\", or match all files. \n") + .append(" --ignore=?: \n") + .append(" A regular expression that all filenames must not match. \n") + .append(" This argument can be escaped with double quotes, ie.g \"[a-zA-z0-9 ]\". \n") + .append(" The regular expression will be applied to the full path of the file. \n") + .append(" Name seperators will be '/' on Unix and ''\\'' on Windows systems. \n") + .append(" This option is not present by default. \n") + .append(" --encoding=? \n") + .append(" If not specified, the encoding of the files is left unchanged. \n") + .append(" Typical values would be UTF-8, UTF-16 or any java-recognized encoding string. \n") + .append(" --line-ending=? \n") + .append(" This can either be WINDOWS or UNIX. \n") + .append(" If not set, the line ending style is left unchanged. \n") + .append(" --replace-tabs=? \n") + .append(" Specify the number of spaces to insert in place of a tab. \n") + .append(" --no-recurse \n") + .append(" Do not recurse into subdirectories. \n") + .append(" --no-backup \n") + .append(" The default is to make a backup of all files prior to modification. \n") + .append(" With this option, no backups are made. \n") + .append(" --dry-run \n") + .append(" Do not modify or backup any files. \n") + .append(" No filesystem modifications are made. \n") + .append(" --verbose \n") + .append(" Dump all files checked to std.out. \n") + .append(" --quiet \n") + .append(" Don't dump anything to std.out. \n") + .append(" directory: \n") + .append(" The directory to start searching in. \n") + .append(" If the directory has spaces in it, then escape it with double quotes, e.g. \"C:\\Program Files\" \n") + .append(" \n") + .append("Details of the modifications being made are written to std.out. \n") + .append("Errors are written to std.err. \n") + .append("When used without any options, this program will behave like a FIND. \n"); + System.out.println(sb); + System.out.flush(); + } +} diff --git a/src/main/java/org/alfresco/util/CronTriggerBean.java b/src/main/java/org/alfresco/util/CronTriggerBean.java new file mode 100644 index 0000000000..25fe78c78c --- /dev/null +++ b/src/main/java/org/alfresco/util/CronTriggerBean.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Date; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.error.AlfrescoRuntimeException; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.Trigger; + +/** + * A utility bean to wrap scheduling a cron job with a given scheduler. + * + * @author Andy Hind + */ +@AlfrescoPublicApi +public class CronTriggerBean extends AbstractTriggerBean +{ + private static final long MILLISECONDS_PER_MINUTE = 60L * 1000L; + + /* + * Milliseconds delay before the job will be triggered. + */ + private long startDelay = 0; + + /* + * The cron expression to trigger execution. + */ + String cronExpression; + + /** + * Default constructor + * + */ + public CronTriggerBean() + { + super(); + } + + /** + * Get the cron expression that determines when this job is run. + * + * @return The cron expression + */ + public String getCronExpression() + { + return cronExpression; + } + + /** + * Set the cron expression that determines when this job is run. + * + * @param cronExpression + */ + public void setCronExpression(String cronExpression) + { + this.cronExpression = cronExpression; + } + + /** + * Build the cron trigger + * + * @return The trigger + * @throws Exception + */ + public Trigger getTrigger() throws Exception + { + Trigger trigger = new CronTrigger(getBeanName(), Scheduler.DEFAULT_GROUP, getCronExpression()); + if (this.startDelay > 0) + { + trigger.setStartTime(new Date(System.currentTimeMillis() + this.startDelay)); + } + JobDetail jd = super.getJobDetail(); + if (jd != null) + { + String jobName = super.getJobDetail().getKey().getName(); + if (jobName != null && !jobName.isEmpty()) + { + trigger.setJobName(jobName); + } + String jobGroup = super.getJobDetail().getKey().getGroup(); + if (jobGroup != null && !jobGroup.isEmpty()) + { + trigger.setJobGroup(jobGroup); + } + } + return trigger; + } + + public long getStartDelay() + { + return startDelay; + } + + public void setStartDelay(long startDelay) + { + this.startDelay = startDelay; + } + + public void setStartDelayMinutes(long startDelayMinutes) + { + this.startDelay = startDelayMinutes * MILLISECONDS_PER_MINUTE; + } + + + public void afterPropertiesSet() throws Exception + { + if ((cronExpression == null) || (cronExpression.trim().length() == 0)) + { + throw new AlfrescoRuntimeException( + "The cron expression has not been set, is zero length, or is all white space"); + } + super.afterPropertiesSet(); + } +} diff --git a/src/main/java/org/alfresco/util/Debug.java b/src/main/java/org/alfresco/util/Debug.java new file mode 100644 index 0000000000..0d3ddf834b --- /dev/null +++ b/src/main/java/org/alfresco/util/Debug.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.net.URL; + +/** + * Class containing debugging utility methods + * + * @author gavinc + */ +public class Debug +{ + /** + * Returns the location of the file that will be loaded for the given class name + * + * @param className The class to load + * @return The location of the file that will be loaded + * @throws ClassNotFoundException + */ + public static String whichClass(String className) throws ClassNotFoundException + { + String path = className; + + // prepare the resource path + if (path.startsWith("/") == false) + { + path = "/" + path; + } + path = path.replace('.', '/'); + path = path + ".class"; + + // get the location + URL url = Debug.class.getResource(path); + if (url == null) + { + throw new ClassNotFoundException(className); + } + + // format the result + String location = url.toExternalForm(); + if (location.startsWith("jar")) + { + location = location.substring(10, location.lastIndexOf("!")); + } + else if (location.startsWith("file:")) + { + location = location.substring(6); + } + + return location; + } + + /** + * Returns the class loader that will load the given class name + * + * @param className The class to load + * @return The class loader the class will be loaded in + * @throws ClassNotFoundException + */ + public static String whichClassLoader(String className) throws ClassNotFoundException + { + String result = "Could not determine class loader for " + className; + + Class clazz = Class.forName(className); + ClassLoader loader = clazz.getClassLoader(); + + if (loader != null) + { + result = clazz.getClassLoader().toString(); + } + + return result; + } + + /** + * Returns the class loader hierarchy that will load the given class name + * + * @param className The class to load + * @return The hierarchy of class loaders used to load the class + * @throws ClassNotFoundException + */ + public static String whichClassLoaderHierarchy(String className) throws ClassNotFoundException + { + StringBuffer buffer = new StringBuffer(); + Class clazz = Class.forName(className); + ClassLoader loader = clazz.getClassLoader(); + if (loader != null) + { + buffer.append(loader.toString()); + + ClassLoader parent = loader.getParent(); + while (parent != null) + { + buffer.append("\n-> ").append(parent.toString()); + parent = parent.getParent(); + } + } + else + { + buffer.append("Could not determine class loader for " + className); + } + + return buffer.toString(); + } +} diff --git a/src/main/java/org/alfresco/util/Deleter.java b/src/main/java/org/alfresco/util/Deleter.java new file mode 100644 index 0000000000..04778260b1 --- /dev/null +++ b/src/main/java/org/alfresco/util/Deleter.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Utility to delete a file or directory recursively. + * @author britt + */ +public class Deleter +{ + private static final Log log = LogFactory.getLog(Deleter.class); + + /** + * Delete by path. + * @param path + */ + public static void Delete(String path) + { + File toDelete = new File(path); + Delete(toDelete); + } + + /** + * Delete by File. + * @param toDelete + */ + public static void Delete(File toDelete) + { + if (toDelete.isDirectory()) + { + File[] listing = toDelete.listFiles(); + for (File file : listing) + { + Delete(file); + } + } + toDelete.delete(); + } + + + /** + * Recursively deletes the parents of the specified file stopping when rootDir is reached. + * The file itself must have been deleted before calling this method - since only empty + * directories can be deleted. + *

+ * For example: deleteEmptyParents(new File("/tmp/a/b/c/d.txt"), "/tmp/a") + *

+ * Will delete directories c and b assuming that they are both empty. It will leave /tmp/a even if it is + * empty as this is the rootDir + * + * @param file The path of the file whose parent directories should be deleted. + * @param rootDir Top level directory where deletion should stop. Must be the canonical path + * to ensure correct comparisons. + */ + public static void deleteEmptyParents(File file, String rootDir) + { + File parent = file.getParentFile(); + boolean deleted = false; + do + { + try + { + if (parent.isDirectory() && !parent.getCanonicalPath().equals(rootDir)) + { + // Only an empty directory will successfully be deleted. + deleted = parent.delete(); + } + } + catch (IOException error) + { + log.error("Unable to construct canonical path for " + parent.getAbsolutePath()); + break; + } + + parent = parent.getParentFile(); + } + while(deleted); + } + + /** + * Same behaviour as for {@link Deleter#deleteEmptyParents(File, String)} but with the + * rootDir parameter specified as a {@link java.io.File} object. + * + * @see Deleter#deleteEmptyParents(File, String) + * @param file + * @param rootDir + */ + public static void deleteEmptyParents(File file, File rootDir) + { + try + { + deleteEmptyParents(file, rootDir.getCanonicalPath()); + } + catch (IOException e) + { + String msg = "Unable to convert rootDir to canonical form [rootDir=" + rootDir + "]"; + throw new RuntimeException(msg, e); + } + } +} diff --git a/src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java b/src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java new file mode 100644 index 0000000000..91a5a513c6 --- /dev/null +++ b/src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This is an instance of {@link java.util.concurrent.ThreadPoolExecutor} which + * behaves how one would expect it to, even when faced with an unlimited + * queue. Unlike the default {@link java.util.concurrent.ThreadPoolExecutor}, it + * will add new Threads up to {@link #setMaximumPoolSize(int) maximumPoolSize} + * when there is lots of pending work, rather than only when the queue is full + * (which it often never will be, especially for unlimited queues) + * + * @author Nick Burch + */ +public class DynamicallySizedThreadPoolExecutor extends ThreadPoolExecutor +{ + private static Log logger = LogFactory.getLog(DynamicallySizedThreadPoolExecutor.class); + + private final ReentrantLock lock = new ReentrantLock(); + private int realCorePoolSize; + + public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, RejectedExecutionHandler handler) + { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); + this.realCorePoolSize = corePoolSize; + } + + public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) + { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); + this.realCorePoolSize = corePoolSize; + } + + public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, ThreadFactory threadFactory) + { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); + this.realCorePoolSize = corePoolSize; + } + + public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue) + { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); + this.realCorePoolSize = corePoolSize; + } + + @Override + public void setCorePoolSize(int corePoolSize) + { + this.realCorePoolSize = corePoolSize; + super.setCorePoolSize(corePoolSize); + } + + @Override + public void execute(Runnable command) + { + // Do we want to add another thread? + int threadCount = getPoolSize(); + if(logger.isDebugEnabled()) + { + logger.debug("Current pool size is " + threadCount + ", real core=" + realCorePoolSize + + ", current core=" + getCorePoolSize() + ", max=" + getMaximumPoolSize()); + } + + if(threadCount < getMaximumPoolSize()) + { + // We're not yet at the full thread count + + // Does the queue size warrant adding one? + // (If there are more than the maximum pool size of jobs pending, + // it's time to add another thread) + int queueSize = getQueue().size() + 1;// New job not yet added + if(queueSize >= getMaximumPoolSize()) + { + lock.lock(); + int currentCoreSize = getCorePoolSize(); + if(currentCoreSize < getMaximumPoolSize()) + { + super.setCorePoolSize(currentCoreSize+1); + + if(logger.isInfoEnabled()) + { + logger.info("Increased pool size to " + getCorePoolSize() + " from " + + currentCoreSize + " due to queue size of " + queueSize); + } + } + lock.unlock(); + } + } + + // Now run the actual work + super.execute(command); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) + { + // If the queue is looking empty, allow the pool to + // get rid of idle threads when it wants to + int threadCount = getPoolSize(); + if(threadCount == getMaximumPoolSize() && threadCount > realCorePoolSize) + { + int queueSize = getQueue().size(); + int currentCoreSize = getCorePoolSize(); + if(queueSize < 2 && currentCoreSize > realCorePoolSize) + { + // Almost out of work, allow the pool to reduce threads when + // required. Double checks the sizing inside a lock to avoid + // race conditions taking us below the real core size. + lock.lock(); + currentCoreSize = getCorePoolSize(); + if(currentCoreSize > realCorePoolSize) + { + super.setCorePoolSize(currentCoreSize-1); + + if(logger.isInfoEnabled()) + { + logger.info("Decreased pool size to " + getCorePoolSize() + " from " + + currentCoreSize + " (real core size is " + realCorePoolSize + + ") due to queue size of " + queueSize); + } + } + lock.unlock(); + } + } + } +} diff --git a/src/main/java/org/alfresco/util/EqualsHelper.java b/src/main/java/org/alfresco/util/EqualsHelper.java new file mode 100644 index 0000000000..7b7c4cc8a1 --- /dev/null +++ b/src/main/java/org/alfresco/util/EqualsHelper.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.api.AlfrescoPublicApi; + +/** + * Utility class providing helper methods for various types of equals functionality + * + * @author Derek Hulley + */ +@AlfrescoPublicApi +public class EqualsHelper +{ + /** + * Performs an equality check left.equals(right) after checking for null values + * + * @param left the Object appearing in the left side of an equals statement + * @param right the Object appearing in the right side of an equals statement + * @return Return true or false even if one or both of the objects are null + */ + public static boolean nullSafeEquals(Object left, Object right) + { + return (left == right) || (left != null && right != null && left.equals(right)); + } + + /** + * Performs an case-sensitive or case-insensitive equality check after checking for null values + * @param ignoreCase true to ignore case + */ + public static boolean nullSafeEquals(String left, String right, boolean ignoreCase) + { + if (ignoreCase) + { + return (left == right) || (left != null && right != null && left.equalsIgnoreCase(right)); + } + else + { + return (left == right) || (left != null && right != null && left.equals(right)); + } + } + + private static final int BUFFER_SIZE = 1024; + /** + * Performs a byte-level comparison between two streams. + * + * @param left the left stream. This is closed at the end of the operation. + * @param right an right stream. This is closed at the end of the operation. + * @return Returns true if the streams are identical to the last byte + */ + public static boolean binaryStreamEquals(InputStream left, InputStream right) throws IOException + { + try + { + if (left == right) + { + // The same stream! This is pretty pointless, but they are equal, nevertheless. + return true; + } + + byte[] leftBuffer = new byte[BUFFER_SIZE]; + byte[] rightBuffer = new byte[BUFFER_SIZE]; + while (true) + { + int leftReadCount = left.read(leftBuffer); + int rightReadCount = right.read(rightBuffer); + if (leftReadCount != rightReadCount) + { + // One stream ended before the other + return false; + } + else if (leftReadCount == -1) + { + // Both streams ended without any differences found + return true; + } + for (int i = 0; i < leftReadCount; i++) + { + if (leftBuffer[i] != rightBuffer[i]) + { + // We found a byte difference + return false; + } + } + } + // The only exits with 'return' statements, so there is no need for any code here + } + finally + { + try { left.close(); } catch (Throwable e) {} + try { right.close(); } catch (Throwable e) {} + } + } + + /** + * Compare two maps and generate a difference report between the actual and expected values. + * This method is particularly useful during unit tests as the result (if not null) + * can be appended to a failure message. + * + * @param actual the map in hand + * @param expected the map expected + * @return Returns a difference report or null if there were no + * differences. The message starts with a new line and is neatly + * formatted. + */ + public static String getMapDifferenceReport(Map actual, Map expected) + { + Map copyResult = new HashMap(actual); + + boolean failure = false; + + StringBuilder sb = new StringBuilder(1024); + sb.append("\nValues that don't match the expected values: "); + for (Map.Entry entry : expected.entrySet()) + { + Object key = entry.getKey(); + Object expectedValue = entry.getValue(); + Object resultValue = actual.get(key); + if (!EqualsHelper.nullSafeEquals(resultValue, expectedValue)) + { + sb.append("\n") + .append(" Key: ").append(key).append("\n") + .append(" Result: ").append(resultValue).append("\n") + .append(" Expected: ").append(expectedValue); + failure = true; + } + copyResult.remove(key); + } + sb.append("\nValues that are present but should not be: "); + for (Map.Entry entry : copyResult.entrySet()) + { + Object key = entry.getKey(); + Object resultValue = entry.getValue(); + sb.append("\n") + .append(" Key: ").append(key).append("\n") + .append(" Result: ").append(resultValue); + failure = true; + } + if (failure) + { + return sb.toString(); + } + else + { + return null; + } + } + + /** + * Enumeration for results returned by {@link EqualsHelper#getMapComparison(Map, Map) map comparisons}. + * + * @author Derek Hulley + * @since 3.3 + */ + public static enum MapValueComparison + { + /** The key was only present in the left map */ + LEFT_ONLY, + /** The key was only present in the right map */ + RIGHT_ONLY, + /** The key was present in both maps and the values were equal */ + EQUAL, + /** The key was present in both maps but not equal */ + NOT_EQUAL + } + + /** + * Compare two maps. + *

+ * The return codes that accompany the keys are: + *

    + *
  • {@link MapValueComparison#LEFT_ONLY}
  • + *
  • {@link MapValueComparison#RIGHT_ONLY}
  • + *
  • {@link MapValueComparison#EQUAL}
  • + *
  • {@link MapValueComparison#NOT_EQUAL}
  • + *
+ * + * @param the map key type + * @param the map value type + * @param left the left side of the comparison + * @param right the right side of the comparison + * @return Returns a map whose keys are a union of the two maps' keys, along with + * the value comparison result + */ + public static Map getMapComparison(Map left, Map right) + { + Set keys = new HashSet(left.size() + right.size()); + keys.addAll(left.keySet()); + keys.addAll(right.keySet()); + + Map diff = new HashMap(left.size() + right.size()); + + // Iterate over the keys and do the comparisons + for (K key : keys) + { + boolean leftHasKey = left.containsKey(key); + boolean rightHasKey = right.containsKey(key); + V leftValue = left.get(key); + V rightValue = right.get(key); + if (leftHasKey) + { + if (!rightHasKey) + { + diff.put(key, MapValueComparison.LEFT_ONLY); + } + else if (EqualsHelper.nullSafeEquals(leftValue, rightValue)) + { + diff.put(key, MapValueComparison.EQUAL); + } + else + { + diff.put(key, MapValueComparison.NOT_EQUAL); + } + } + else if (rightHasKey) + { + if (!leftHasKey) + { + diff.put(key, MapValueComparison.RIGHT_ONLY); + } + else if (EqualsHelper.nullSafeEquals(leftValue, rightValue)) + { + diff.put(key, MapValueComparison.EQUAL); + } + else + { + diff.put(key, MapValueComparison.NOT_EQUAL); + } + } + else + { + // How is it here? + } + } + + return diff; + } +} diff --git a/src/main/java/org/alfresco/util/ExpiringValueCache.java b/src/main/java/org/alfresco/util/ExpiringValueCache.java new file mode 100644 index 0000000000..d888628748 --- /dev/null +++ b/src/main/java/org/alfresco/util/ExpiringValueCache.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.Serializable; + +/** + * Simple cache of a single Object value. + *

+ * The object placed in the cache will automatically be discarded after a timeout value. + * + * @author Kevin Roast + */ +public class ExpiringValueCache implements Serializable +{ + private static final long serialVersionUID = 1036233352030777619L; + + // default is to discard cached object after 1 minute + private final static long TIMEOUT_DEFAULT = 1000L*60L; + + private long timeout = TIMEOUT_DEFAULT; + private long snapshot = 0; + private T value; + + /** + * Default constructor. + * + * Uses the default timeout of 1 minute. + */ + public ExpiringValueCache() + { + } + + /** + * Constructor + * + * @param timeout Timeout in milliseconds before cached value is discarded + */ + public ExpiringValueCache(long timeout) + { + this.timeout = timeout; + } + + /** + * Put a value into the cache. The item will be return from the associated get() method + * until the timeout expires then null will be returned. + * + * @param value The object to store in the cache + */ + public void put(T value) + { + this.value = value; + this.snapshot = System.currentTimeMillis(); + } + + /** + * Get the cached object. The set item will be returned until it expires, then null will be returned. + * + * @return cached object or null if not set or expired. + */ + public T get() + { + if (snapshot + timeout < System.currentTimeMillis()) + { + this.value = null; + } + return this.value; + } + + /** + * Clear the cache value + */ + public void clear() + { + this.value = null; + } +} diff --git a/src/main/java/org/alfresco/util/FileFilterMode.java b/src/main/java/org/alfresco/util/FileFilterMode.java new file mode 100644 index 0000000000..3d7e488f91 --- /dev/null +++ b/src/main/java/org/alfresco/util/FileFilterMode.java @@ -0,0 +1,115 @@ +package org.alfresco.util; + +public class FileFilterMode +{ + /** + * Clients for which specific hiding/visibility behaviour may be requested. + * Do not remove or change the order of + */ + public static enum Client + { + cifs, imap, webdav, nfs, script, webclient, ftp, cmis, admin; + + /** + * @deprecated Use {@link Client#valueOf(String)} + */ + @Deprecated + public static Client getClient(String clientStr) + { + if(clientStr.equals("cifs")) + { + return cifs; + } + else if(clientStr.equals("imap")) + { + return imap; + } + else if(clientStr.equals("webdav")) + { + return webdav; + } + else if(clientStr.equals("nfs")) + { + return nfs; + } + else if(clientStr.equals("ftp")) + { + return ftp; + } + else if(clientStr.equals("script")) + { + return script; + } + else if(clientStr.equals("webclient")) + { + return webclient; + } + else if(clientStr.equals("cmis")) + { + return cmis; + } + else if(clientStr.equals("admin")) + { + return admin; + } + else + { + throw new IllegalArgumentException(); + } + } + }; + + public static enum Mode + { + BASIC, ENHANCED; + }; + + private static ThreadLocal client = new ThreadLocal() + { + protected Client initialValue() { + return null; + } + }; + + public static void clearClient() + { + client.set(null); + } + + public static Client setClient(Client newClient) + { + Client oldClient = client.get(); + client.set(newClient); + + return oldClient; + } + + public static Mode getMode() + { + Client client = getClient(); + if(client == null) + { + return Mode.BASIC; + } + else + { + switch(client) + { + case cifs : + case nfs : + case ftp : + case webdav : + case cmis : + case admin : + return Mode.ENHANCED; + default: + return Mode.BASIC; + } + } + } + + public static Client getClient() + { + return client.get(); + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/GUID.java b/src/main/java/org/alfresco/util/GUID.java new file mode 100644 index 0000000000..227e928a5e --- /dev/null +++ b/src/main/java/org/alfresco/util/GUID.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.security.SecureRandom; +import java.util.Random; + +import org.safehaus.uuid.UUIDGenerator; +import org.alfresco.api.AlfrescoPublicApi; + +/** + * A wrapper class to serve up GUIDs + * + * @author kevinr + */ +@AlfrescoPublicApi +public final class GUID +{ + /** + * Private Constructor for GUID. + */ + private GUID() + { + } + +// protected static final char[] s_values = +// { +// '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', +// 'f' +// }; + + private static final SecureRandom[] SECURE_RANDOM_POOL = initSecureRandomArray(); + private static final int SECURE_RANDOM_POOL_MAX_ITEMS = 256; + private static final Random RANDOM = new Random(); + + + private static SecureRandom[] initSecureRandomArray() + { + SecureRandom[] array = new SecureRandom[SECURE_RANDOM_POOL_MAX_ITEMS]; + for (int i = 0; i < SECURE_RANDOM_POOL_MAX_ITEMS; i++) + { + array[i] = new SecureRandom(); + } + return array; + } + + /** + * Generates and returns a new GUID as a string based on a SecureRandom pool in other to avoid + * thread blocking in concurrent calls. + * + * @return String GUID + */ + public static String generate() + { + int randomInt = RANDOM.nextInt(SECURE_RANDOM_POOL_MAX_ITEMS); + return UUIDGenerator.getInstance().generateRandomBasedUUID(SECURE_RANDOM_POOL[randomInt]).toString(); + } + +// == Not sure if we need this functionality again (derekh) == +// +// /** +// * Convert a string with a guid inside into a byte[16] array +// * +// * @param str - the guid +// * @return - byte[16] containing the GUID +// * @throws InvalidGuidFormatException +// */ +// public static byte[] parseFromString(String str) throws InvalidGuidFormatException +// { +// byte[] data = new byte[16]; +// int dataPos = 0; +// +// byte bVal; +// int value = 0; +// int pos = 0; +// +// for(int i = 0; i < str.length(); i++) +// { +// char thisChar = str.charAt(i); +// +// int idx = 0; +// +// if(thisChar >= '0' && thisChar <= '9') +// { +// idx = thisChar - '0'; +// pos++; +// } +// else if(thisChar >= 'a' && thisChar <= 'f') +// { +// idx = thisChar - 'a' + 10; +// pos++; +// } +// else if(thisChar >= 'a' && thisChar <= 'f') +// { +// idx = thisChar - 'A' + 10; +// pos++; +// } +// else if(thisChar == '-' || thisChar == '{' || thisChar == '}') +// { +// // Doesn't matter +// } +// else +// { +// throw new InvalidGuidFormatException(); +// } +// +// try +// { +// if(pos == 1) +// value = idx; +// else if(pos == 2) +// { +// value = (value * 16) + idx; +// +// byte b = (byte) value; +// data[dataPos++] = b; +// +// pos = 0; +// } +// } +// catch(RuntimeException e) +// { +// // May occur if we go off the end of the data index +// throw new InvalidGuidFormatException(); +// } +// } +// +// return data; +// } +// +// /** +// * Convert a byte[16] containing a guid to a string representation +// * +// * @param data - the data +// * @return - the string +// */ +// public static String convertToString(byte[] data) +// { +// char[] output = new char[36]; +// int cPos = 0; +// +// for(int i = 0; i < 16; i++) +// { +// int v = data[i]; +// +// int lowVal = v & 0x000F; +// int hiVal = (v & 0x00F0) >> 4; +// +// output[cPos++] = s_values[hiVal]; +// output[cPos++] = s_values[lowVal]; +// +// if(cPos == 8 || cPos == 13 || cPos == 18 || cPos == 23) +// output[cPos++] = '-'; +// } +// +// return new String(output); +// } +} diff --git a/src/main/java/org/alfresco/util/IPUtils.java b/src/main/java/org/alfresco/util/IPUtils.java new file mode 100644 index 0000000000..1ddb9c928e --- /dev/null +++ b/src/main/java/org/alfresco/util/IPUtils.java @@ -0,0 +1,26 @@ +package org.alfresco.util; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class IPUtils +{ + /** + * Returns the "real" IP address represented by ipAddress. If ipAddress is a loopback + * address it is converted into the host's underlying IP address + * + * @param ipAddress String + * @return String + * @throws UnknownHostException + */ + public static String getRealIPAddress(String ipAddress) throws UnknownHostException + { + if(ipAddress.equals("localhost") || ipAddress.equals("127.0.0.1")) + { + // make sure we are using a "real" IP address + ipAddress = InetAddress.getLocalHost().getHostAddress(); + } + + return ipAddress; + } +} diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/src/main/java/org/alfresco/util/ISO8601DateFormat.java new file mode 100644 index 0000000000..ca0a460d7e --- /dev/null +++ b/src/main/java/org/alfresco/util/ISO8601DateFormat.java @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2005-2016 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.error.AlfrescoRuntimeException; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.IllegalInstantException; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.ISODateTimeFormat; + + +/** + * Formatting support for ISO 8601 dates + *

+ *    sYYYY-MM-DDThh:mm:ss.sssTZD
+ * 
+ * where: + *
    + *
  • sYYYY Four-digit year with optional leading positive (+) or negative (-) sign. + * A negative sign indicates a year BCE. The absence of a sign or the presence of a + * positive sign indicates a year CE (for example, -0055 would indicate the year 55 BCE, + * while +1969 and 1969 indicate the year 1969 CE).
  • + *
  • MM Two-digit month (01 = January, etc.)
  • + *
  • DD Two-digit day of month (01 through 31)
  • + *
  • hh Two digits of hour (00 through 23)
  • + *
  • mm Two digits of minute (00 through 59)
  • + *
  • ss.sss Seconds, to three decimal places (00.000 through 59.999)
  • + *
  • TZD Time zone designator (either Z for Zulu, i.e. UTC, or +hh:mm or -hh:mm, i.e. an offset from UTC)
  • + *
+ */ +@AlfrescoPublicApi +public class ISO8601DateFormat +{ + private static ThreadLocal> calendarThreadLocal = new ThreadLocal>(); + /** + * Get a calendar object from cache. + * @return calendar object from cache or newly created (if cache is empty) + */ + public static Calendar getCalendar() + { + if (calendarThreadLocal.get() == null) + { + calendarThreadLocal.set(new HashMap()); + } + + Calendar calendar = calendarThreadLocal.get().get(TimeZone.getDefault()); + if (calendar == null) + { + calendar = new GregorianCalendar(); + calendarThreadLocal.get().put(TimeZone.getDefault(), calendar); + } + + return calendar; + } + + /** + * Format date into ISO format (UCT0 / Zulu) + * + * @param isoDate the date to format + * @return the ISO Zulu timezone formatted string + */ + public static String format(Date isoDate) + { + Calendar calendar = getCalendar(); + calendar.setTime(isoDate); + + // MNT-9790 + // org.joda.time.DateTime.DateTime take away some minutes from date before 1848 year at formatting. + // This behavior connected with acceptance of time zones based + // on the Greenwich meridian (it was in Great Britain, year 1848). + if (calendar.get(Calendar.YEAR) > 1847) + { + DateTime dt = new DateTime(isoDate, DateTimeZone.UTC); + return dt.toString(); + } + else + { + int val = 0; + StringBuilder formatted = new StringBuilder(28); + formatted.append(calendar.get(Calendar.YEAR)); + formatted.append('-'); + val = calendar.get(Calendar.MONTH) + 1; + formatted.append(val < 10 ? ("0" + val) : val); + formatted.append('-'); + val = calendar.get(Calendar.DAY_OF_MONTH); + formatted.append(val < 10 ? ("0" + val) : val); + formatted.append('T'); + val = calendar.get(Calendar.HOUR_OF_DAY); + formatted.append(val < 10 ? ("0" + val) : val); + formatted.append(':'); + val = calendar.get(Calendar.MINUTE); + formatted.append(val < 10 ? ("0" + val) : val); + formatted.append(':'); + val = calendar.get(Calendar.SECOND); + formatted.append(val < 10 ? ("0" + val) : val); + formatted.append('.'); + val = calendar.get(Calendar.MILLISECOND); + if (val < 10) + { + formatted.append(val < 10 ? ("00" + val) : val); + } + else if (val >= 10 && val < 100) + { + formatted.append(val < 10 ? ("0" + val) : val); + } + else + { + formatted.append(val); + } + + TimeZone tz = calendar.getTimeZone(); + int offset = tz.getOffset(calendar.getTimeInMillis()); + if (offset != 0) + { + int hours = Math.abs((offset / (60 * 1000)) / 60); + int minutes = Math.abs((offset / (60 * 1000)) % 60); + formatted.append(offset < 0 ? '-' : '+'); + formatted.append(hours < 10 ? ("0" + hours) : hours); + formatted.append(':'); + formatted.append(minutes < 10 ? ("0" + minutes) : minutes); + } + else + { + formatted.append('Z'); + } + + return formatted.toString(); + } + } + + /** + * Normalise isoDate time to Zulu(UTC0) time-zone, removing any UTC offset. + * @param isoDate + * @return the ISO Zulu timezone formatted string + * e.g 2011-02-04T17:13:14.000+01:00 -> 2011-02-04T16:13:14.000Z + */ + public static String formatToZulu(String isoDate) + { + try + { + DateTime dt = new DateTime(isoDate, DateTimeZone.UTC); + return dt.toString(); + } catch (IllegalArgumentException e) + { + throw new AlfrescoRuntimeException("Failed to parse date " + isoDate, e); + } + } + + /** + * Parse date from ISO formatted string. + * The ISO8601 date must include TimeZone offset information + * + * @param isoDate ISO string to parse + * @return the date + * @throws AlfrescoRuntimeException if the parse failed + */ + public static Date parse(String isoDate) + { + return parseInternal(isoDate, null); + } + + /** + * Parse date from ISO formatted string, with an + * explicit timezone specified + * + * @param isoDate ISO string to parse + * @param timezone The TimeZone the date is in + * @return the date + * @throws AlfrescoRuntimeException if the parse failed + */ + public static Date parse(String isoDate, TimeZone timezone) + { + return parseInternal(isoDate, timezone); + } + + /** + * Parse date from ISO formatted string, either in the specified + * TimeZone, or with TimeZone information taken from the date + * + * @param isoDate ISO string to parse + * @param timezone The time zone, null means default time zone + * @return the date + * @throws AlfrescoRuntimeException if the parse failed + */ + public static Date parseInternal(String isoDate, TimeZone timezone) + { + try + { + // null time-zone defaults to the local time-zone + DateTimeZone dtz = DateTimeZone.forTimeZone(timezone); + try + { + DateTime dateTime = new DateTime(isoDate, dtz); + Date date = dateTime.toDate(); + return date; + } + catch (IllegalInstantException ie) + { + // The exception is thrown when a DateTime was created with a date-time inside the DST gap - a time that did not exist. + // Parse the date ignoring the time. + DateTimeFormatter parser = ISODateTimeFormat.dateTimeParser(); + LocalDate ldate = new LocalDate(parser.parseLocalDate(isoDate), dtz); + // Default to the first valid date-time of the day, not always 00:00 (because of DST). + DateTime dateT = ldate.toDateTimeAtStartOfDay(dtz); + Date date = dateT.toDate(); + return date; + } + } + catch (IllegalArgumentException e) + { + throw new AlfrescoRuntimeException("Failed to parse date " + isoDate, e); + } + } + + /** + * Checks whether or not the given ISO8601-formatted date-string contains a time-component + * instead of only the actual date. + * + * @param isoDate + * @return true, if time is present. + */ + public static boolean isTimeComponentDefined(String isoDate) + { + boolean defined = false; + + if(isoDate != null && isoDate.length() > 11) + { + // Find occurrence of T (sYYYY-MM-DDT..), sign is optional + int expectedLocation = 10; + if(isoDate.charAt(0) == '-' || isoDate.charAt(0) == '+') { + // Sign is included before year + expectedLocation++; + } + + defined = isoDate.length() >= expectedLocation && isoDate.charAt(expectedLocation) == 'T'; + } + + return defined; + } + + /** + * Parses the given ISO8601-formatted date-string, not taking into account the time-component. + * The time-information for the will be reset to zero. + * + * @param isoDate the day (formatted sYYYY-MM-DD) or a full date (sYYYY-MM-DDThh:mm:ss.sssTZD) + * @param timezone the timezone to use + * @return the parsed date + * + * @throws AlfrescoRuntimeException if the parsing failed. + */ + public static Date parseDayOnly(String isoDate, TimeZone timezone) + { + try + { + if(isoDate != null && isoDate.length() >= 10) + { + int offset = 0; + + // Sign can be included before year + boolean bc = false; + if(isoDate.charAt(0) == '-') + { + bc = true; + offset++; + } + else if(isoDate.charAt(0) == '+') + { + offset++; + } + + // Extract year + int year = Integer.parseInt(isoDate.substring(offset, offset += 4)); + if (isoDate.charAt(offset) != '-') + { + throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset)); + } + + // Extract month + int month = Integer.parseInt(isoDate.substring(offset += 1, offset += 2)); + if (isoDate.charAt(offset) != '-') + { + throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset)); + } + + // Extract day + int day = Integer.parseInt(isoDate.substring(offset += 1, offset += 2)); + + Calendar calendar = new GregorianCalendar(timezone); + calendar.setLenient(false); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + if(bc) + { + calendar.set(Calendar.ERA, GregorianCalendar.BC); + } + + return calendar.getTime(); + } + else + { + throw new AlfrescoRuntimeException("String passed is too short " + isoDate); + } + } + catch(IndexOutOfBoundsException e) + { + throw new AlfrescoRuntimeException("Failed to parse date " + isoDate, e); + } + catch(NumberFormatException e) + { + throw new AlfrescoRuntimeException("Failed to parse date " + isoDate, e); + } + } + + +} diff --git a/src/main/java/org/alfresco/util/InputStreamContent.java b/src/main/java/org/alfresco/util/InputStreamContent.java new file mode 100644 index 0000000000..7e497bf7b5 --- /dev/null +++ b/src/main/java/org/alfresco/util/InputStreamContent.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; + +import org.springframework.util.FileCopyUtils; + + +/** + * Input Stream based Content + */ +public class InputStreamContent implements Content, Serializable +{ + private static final long serialVersionUID = -7729633986840536282L; + + private InputStream stream; + private String mimetype; + private String encoding; + + /** cached result - to ensure we only read it once */ + private String content; + + + /** + * Constructor + * + * @param stream content input stream + * @param mimetype content mimetype + */ + public InputStreamContent(InputStream stream, String mimetype, String encoding) + { + this.stream = stream; + this.mimetype = mimetype; + this.encoding = encoding; + } + + /* (non-Javadoc) + * @see org.alfresco.util.Content#getContent() + */ + public String getContent() + throws IOException + { + // ensure we only try to read the content once - as this method may be called several times + // but the inputstream can only be processed a single time + if (this.content == null) + { + ByteArrayOutputStream os = new ByteArrayOutputStream(1024); + FileCopyUtils.copy(stream, os); // both streams are closed + byte[] bytes = os.toByteArray(); + // get the encoding for the string + String encoding = getEncoding(); + // create the string from the byte[] using encoding if necessary + this.content = (encoding == null) ? new String(bytes) : new String(bytes, encoding); + } + return this.content; + } + + /* (non-Javadoc) + * @see org.alfresco.util.Content#getInputStream() + */ + public InputStream getInputStream() + { + return stream; + } + + + public Reader getReader() + throws IOException + { + return (encoding == null) ? new InputStreamReader(stream) : new InputStreamReader(stream, encoding); + } + + /* (non-Javadoc) + * @see org.alfresco.util.Content#getSize() + */ + public long getSize() + { + return -1; + } + + /* (non-Javadoc) + * @see org.alfresco.util.Content#getMimetype() + */ + public String getMimetype() + { + return mimetype; + } + + /* (non-Javadoc) + * @see org.alfresco.util.Content#getEncoding() + */ + public String getEncoding() + { + return encoding; + } + +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/JMXUtils.java b/src/main/java/org/alfresco/util/JMXUtils.java new file mode 100644 index 0000000000..e64de9c850 --- /dev/null +++ b/src/main/java/org/alfresco/util/JMXUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Date; + +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +public class JMXUtils +{ + public static OpenType getOpenType(Object o) + { + if(o instanceof Long) + { + return SimpleType.LONG; + } + else if(o instanceof String) + { + return SimpleType.STRING; + } + else if(o instanceof Date) + { + return SimpleType.DATE; + } + else if(o instanceof Integer) + { + return SimpleType.INTEGER; + } + else if(o instanceof Boolean) + { + return SimpleType.BOOLEAN; + } + else if(o instanceof Double) + { + return SimpleType.DOUBLE; + } + else if(o instanceof Float) + { + return SimpleType.FLOAT; + } + else + { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/org/alfresco/util/LockHelper.java b/src/main/java/org/alfresco/util/LockHelper.java new file mode 100644 index 0000000000..5d64241a92 --- /dev/null +++ b/src/main/java/org/alfresco/util/LockHelper.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +/** + * Helper to make trying for read-write locks simpler + * + * @author Derek Hulley + * @since 4.1.7 + */ +public class LockHelper +{ + /** + * Exception generated when a lock try is unsuccessful + * + * @author Derek Hulley + * @since 4.1.7 + */ + public static class LockTryException extends RuntimeException + { + private static final long serialVersionUID = -3629889029591630609L; + + public LockTryException(String msg) + { + super(msg); + } + } + + /** + * Try to get a lock in the given number of milliseconds or get an exception + * + * @param lock the lock to try + * @param timeoutMs the number of milliseconds to try + * @param useCase {@link String} value which specifies description of use case when lock is needed + * @throws LockTryException the exception if the time is exceeded or the thread is interrupted + */ + public static void tryLock(Lock lock, long timeoutMs, String useCase) throws LockTryException + { + boolean gotLock = false; + try + { + gotLock = lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + // Handled + } + if (!gotLock) + { + throw new LockTryException("Failed to get lock " + lock.getClass().getSimpleName() + " for " + useCase + " in " + timeoutMs + "ms."); + } + } +} diff --git a/src/main/java/org/alfresco/util/LogAdapter.java b/src/main/java/org/alfresco/util/LogAdapter.java new file mode 100644 index 0000000000..ae27069ddd --- /dev/null +++ b/src/main/java/org/alfresco/util/LogAdapter.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.apache.commons.logging.Log; + +import org.alfresco.api.AlfrescoPublicApi; + +/** + * Utility class to adapt a {@link Log} class. + * + * @since 4.2 + * + * @author Alan Davis + */ +@AlfrescoPublicApi +public abstract class LogAdapter implements Log +{ + final protected Log log; + + /** + * Constructor of an optional wrapped {@link Log}. + * @param log + */ + protected LogAdapter(Log log) + { + this.log = log; + } + + @Override + public void trace(Object arg0) + { + trace(arg0, null); + } + + @Override + public void trace(Object arg0, Throwable arg1) + { + if (log != null) + { + log.trace(arg0, arg1); + } + } + + @Override + public void debug(Object arg0) + { + debug(arg0, null); + } + + @Override + public void debug(Object arg0, Throwable arg1) + { + if (log != null) + { + log.debug(arg0, arg1); + } + } + + @Override + public void info(Object arg0) + { + info(arg0, null); + } + + @Override + public void info(Object arg0, Throwable arg1) + { + if (log != null) + { + log.info(arg0, arg1); + } + } + + @Override + public void warn(Object arg0) + { + warn(arg0, null); + } + + @Override + public void warn(Object arg0, Throwable arg1) + { + if (log != null) + { + log.warn(arg0, arg1); + } + } + + @Override + public void error(Object arg0) + { + error(arg0, null); + } + + @Override + public void error(Object arg0, Throwable arg1) + { + if (log != null) + { + log.error(arg0, arg1); + } + } + + @Override + public void fatal(Object arg0) + { + fatal(arg0, null); + } + + @Override + public void fatal(Object arg0, Throwable arg1) + { + if (log != null) + { + log.fatal(arg0, arg1); + } + } + + @Override + public boolean isTraceEnabled() + { + return log != null && log.isTraceEnabled(); + } + + @Override + public boolean isDebugEnabled() + { + return log != null && log.isDebugEnabled(); + } + + @Override + public boolean isInfoEnabled() + { + return log != null && log.isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() + { + return log != null && log.isWarnEnabled(); + } + + @Override + public boolean isErrorEnabled() + { + return log != null && log.isErrorEnabled(); + } + + @Override + public boolean isFatalEnabled() + { + return log != null && log.isFatalEnabled(); + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/LogTee.java b/src/main/java/org/alfresco/util/LogTee.java new file mode 100644 index 0000000000..512f95a5b8 --- /dev/null +++ b/src/main/java/org/alfresco/util/LogTee.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.apache.commons.logging.Log; + +/** + * Utility class to split or 'tee' two {@link Log} classes. + * + * @since 4.2 + * + * @author Alan Davis + */ +public class LogTee extends LogAdapter +{ + protected Log log2; + + public LogTee(Log log1, Log log2) + { + super(log1); + this.log2 = log2; + } + + @Override + public void trace(Object arg0, Throwable arg1) + { + log.trace(arg0, arg1); + log2.trace(arg0, arg1); + } + + @Override + public void debug(Object arg0, Throwable arg1) + { + log.debug(arg0, arg1); + log2.debug(arg0, arg1); + } + + @Override + public void info(Object arg0, Throwable arg1) + { + log.info(arg0, arg1); + log2.info(arg0, arg1); + } + + @Override + public void warn(Object arg0, Throwable arg1) + { + log.warn(arg0, arg1); + log2.warn(arg0, arg1); + } + + @Override + public void error(Object arg0, Throwable arg1) + { + log.error(arg0, arg1); + log2.error(arg0, arg1); + } + + @Override + public void fatal(Object arg0, Throwable arg1) + { + log.fatal(arg0, arg1); + log2.fatal(arg0, arg1); + } + + @Override + public boolean isTraceEnabled() + { + return log.isTraceEnabled() || log2.isTraceEnabled(); + } + + @Override + public boolean isDebugEnabled() + { + return log.isDebugEnabled() || log2.isDebugEnabled(); + } + + @Override + public boolean isInfoEnabled() + { + return log.isInfoEnabled() || log2.isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() + { + return log.isWarnEnabled() || log2.isWarnEnabled(); + } + + @Override + public boolean isErrorEnabled() + { + return log.isErrorEnabled() || log2.isErrorEnabled(); + } + + @Override + public boolean isFatalEnabled() + { + return log.isFatalEnabled() || log2.isFatalEnabled(); + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/LogUtil.java b/src/main/java/org/alfresco/util/LogUtil.java new file mode 100644 index 0000000000..515a2938f9 --- /dev/null +++ b/src/main/java/org/alfresco/util/LogUtil.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.springframework.extensions.surf.util.I18NUtil; +import org.apache.commons.logging.Log; + +/** + * Utility class to assist with I18N of log messages. + *

+ * Calls to this class should still be wrapped with the appropriate log level checks: + *

+ * if (logger.isDebugEnabled())
+ * {
+ *     LogUtil.debug(logger, MSG_EXECUTING_STATEMENT, sql);
+ * }
+ * 
+ * + * @see org.springframework.extensions.surf.util.I18NUtil + * @since 2.1 + * + * @author Derek Hulley + */ +public class LogUtil +{ + /** + * Log an I18Nized message to DEBUG. + * + * @param logger the logger to use + * @param messageKey the message key + * @param args the required message arguments + */ + public static final void debug(Log logger, String messageKey, Object ... args) + { + logger.debug(I18NUtil.getMessage(messageKey, args)); + } + + /** + * Log an I18Nized message to INFO. + * + * @param logger the logger to use + * @param messageKey the message key + * @param args the required message arguments + */ + public static final void info(Log logger, String messageKey, Object ... args) + { + logger.info(I18NUtil.getMessage(messageKey, args)); + } + + /** + * Log an I18Nized message to WARN. + * + * @param logger the logger to use + * @param messageKey the message key + * @param args the required message arguments + */ + public static final void warn(Log logger, String messageKey, Object ... args) + { + logger.warn(I18NUtil.getMessage(messageKey, args)); + } + + /** + * Log an I18Nized message to ERROR. + * + * @param logger the logger to use + * @param messageKey the message key + * @param args the required message arguments + */ + public static final void error(Log logger, String messageKey, Object ... args) + { + logger.error(I18NUtil.getMessage(messageKey, args)); + } + + /** + * Log an I18Nized message to ERROR with a given source error. + * + * @param logger the logger to use + * @param e the exception cause of the issue + * @param messageKey the message key + * @param args the required message arguments + */ + public static final void error(Log logger, Throwable e, String messageKey, Object ... args) + { + logger.error(I18NUtil.getMessage(messageKey, args), e); + } +} diff --git a/src/main/java/org/alfresco/util/MD5.java b/src/main/java/org/alfresco/util/MD5.java new file mode 100644 index 0000000000..aee2761744 --- /dev/null +++ b/src/main/java/org/alfresco/util/MD5.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util; +import java.security.*; + +/** +* The MD5 utility class computes the MD5 digest (aka: "hash") of a block +* of data; an MD5 digest is a 32-char ASCII string. +* +* The synchronized/static function "Digest" is useful for situations where +* lock contention in the application is not expected to be an issue. +* +* The unsynchronized/non-static method "digest" is useful in a +* multi-threaded program that wanted to avoid locking by creating +* an MD5 object for exclusive use by a single thread. +* +* +*
+*  EXAMPLE 1:  Static usage
+*
+*      import org..alfresco.util.MD5;
+*      String x = MD5.Digest("hello".getBytes());
+*
+*
+*  EXAMPLE 2:  Per-thread non-static usage
+*
+*      import org..alfresco.util.MD5;
+*      MD5 md5 = new MD5();
+*      ...
+*      String x = md5.digest("hello".getBytes());
+*
+* 
+*/ +public class MD5 +{ + private static final byte[] ToHex_ = + { '0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f' + }; + + private MessageDigest md5_ = null; + + static private MessageDigest Md5_; + static + { + try { Md5_ = MessageDigest.getInstance("MD5");} // MD5 is supported + catch ( NoSuchAlgorithmException e ) {}; // safe to swallow + }; + + /** + * Constructor for use with the unsynchronized/non-static method + * "digest" method. Note that the "digest" function is not + * thread-safe, so if you want to use it, every thread must create + * its own MD5 instance. If you don't want to bother & are willing + * to deal with the potential for lock contention, use the synchronized + * static "Digest" function instead of creating an instance via this + * constructor. + */ + public MD5() + { + try { md5_ = MessageDigest.getInstance("MD5");} // MD5 is supported + catch ( NoSuchAlgorithmException e ) {}; // safe to swallow + } + + /** + * Thread-safe static digest (hashing) function. + * + * If you want to avoid lock contention, create an instance of MD5 + * per-thead, anc call the unsynchronized method 'digest' instead. + */ + public static synchronized String Digest(byte[] dataToHash) + { + Md5_.update(dataToHash, 0, dataToHash.length); + return HexStringFromBytes( Md5_.digest() ); + } + + /** + * Non-threadsafe MD5 digest (hashing) function + */ + public String digest(byte[] dataToHash) + { + md5_.update(dataToHash, 0, dataToHash.length); + return HexStringFromBytes( md5_.digest() ); + } + + private static String HexStringFromBytes(byte[] b) + { + byte [] hex_bytes = new byte[ b.length * 2 ]; + int i=0,j=0; + + for (i=0; i < b.length; i++) + { + hex_bytes[j] = ToHex_[ ( b[i] & 0x000000F0 ) >> 4 ] ; + hex_bytes[j+1] = ToHex_[ b[i] & 0x0000000F ]; + j+=2; + } + return new String( hex_bytes ); + } +} diff --git a/src/main/java/org/alfresco/util/MaxSizeMap.java b/src/main/java/org/alfresco/util/MaxSizeMap.java new file mode 100644 index 0000000000..bc402bca39 --- /dev/null +++ b/src/main/java/org/alfresco/util/MaxSizeMap.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Map that ejects the last recently accessed or inserted element(s) to keep the size to a specified maximum. + * + * @param + * Key + * @param + * Value + */ +public class MaxSizeMap extends LinkedHashMap +{ + private static final long serialVersionUID = 3753219027867262507L; + + private final int maxSize; + + /** + * @param maxSize maximum size of the map. + * @param accessOrder true for access-order, false for insertion-order. + */ + public MaxSizeMap(int maxSize, boolean accessOrder) + { + super(maxSize * 2, 0.75f, accessOrder); + this.maxSize = maxSize; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return super.size() > this.maxSize; + } +} diff --git a/src/main/java/org/alfresco/util/OneToManyBiMap.java b/src/main/java/org/alfresco/util/OneToManyBiMap.java new file mode 100644 index 0000000000..5d49ec19ca --- /dev/null +++ b/src/main/java/org/alfresco/util/OneToManyBiMap.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util; + +/** + * An extension of org.alfresco.util.OneToManyMap that stores the + * inverse mapping from a value to its key. + * + * @author Nick Smith + */ +public interface OneToManyBiMap extends OneToManyMap +{ + + /** + * Returns the key, if any, for the specified value. If the + * specified value does not exist within the map then this method returns + * null. + * + * @param value + * @return The key to the specified value or null. + */ + public abstract K getKey(V value); + + /** + * Removes the specified value from the OneToManyBiMap. If this was the only value associated with the key to this value, then the key is also removed. + * + * @param value The value to be removed. + * @return The key that is associated with the value to be removed. + */ + public abstract K removeValue(V value); + +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/OneToManyHashBiMap.java b/src/main/java/org/alfresco/util/OneToManyHashBiMap.java new file mode 100644 index 0000000000..57dd0937dd --- /dev/null +++ b/src/main/java/org/alfresco/util/OneToManyHashBiMap.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author Nick Smith + */ +public class OneToManyHashBiMap implements Map>, OneToManyBiMap +{ + // The 'forward' map. + private OneToManyHashMap map = new OneToManyHashMap(); + + // The inverse map. + private Map inverse = new HashMap(); + + public void clear() + { + map.clear(); + inverse.clear(); + } + + public boolean containsKey(Object key) + { + return map.containsKey(key); + } + + public boolean containsValue(Object value) + { + return map.containsValue(value); + } + + public boolean containsSingleValue(V value) + { + return inverse.containsKey(value); + } + + public Set>> entrySet() + { + return map.entrySet(); + } + + public Set> entries() + { + return map.entries(); + } + + public Set get(Object key) + { + return map.get(key); + } + + /* + * @see org.alfresco.util.OneToManyBiMap#getKey(V) + */ + public K getKey(V value) + { + return inverse.get(value); + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public Set keySet() + { + return map.keySet(); + } + + public Set put(K key, Set values) + { + map.put(key, values); + for (V value : values) + { + inverse.put(value, key); + } + return null; + } + + public V putSingleValue(K key, V value) + { + inverse.put(value, key); + return map.putSingleValue(key, value); + } + + public void putAll(Map> m) + { + map.putAll(m); + for (Entry> entry : m.entrySet()) + { + K key = entry.getKey(); + for (V value : entry.getValue()) + { + inverse.put(value, key); + } + } + } + + public void putAllSingleValues(Map m) + { + map.putAllSingleValues(m); + for (Entry entry : m.entrySet()) + { + inverse.put(entry.getValue(), entry.getKey()); + } + } + + public Set remove(Object key) + { + Set values = map.remove(key); + for (V value : values) + { + inverse.remove(value); + } + return values; + } + + /* + * @see org.alfresco.util.OneToManyBiMap#removeValue(V) + */ + public K removeValue(V value) + { + K key = inverse.remove(value); + Set values = map.get(key); + values.remove(value); + if (values.size() == 0) map.remove(key); + return key; + } + + public int size() + { + return map.size(); + } + + public Collection> values() + { + return map.values(); + } + + public Collection flatValues() + { + return Collections.unmodifiableCollection(inverse.keySet()); + } + +} diff --git a/src/main/java/org/alfresco/util/OneToManyHashMap.java b/src/main/java/org/alfresco/util/OneToManyHashMap.java new file mode 100644 index 0000000000..da13105ca1 --- /dev/null +++ b/src/main/java/org/alfresco/util/OneToManyHashMap.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; + +/** + * @author Nick Smith + */ +public class OneToManyHashMap implements Map>, OneToManyMap +{ + //Delegate map. + private final Map> map = new HashMap>(); + + public void clear() + { + map.clear(); + } + + public boolean containsKey(Object key) + { + return map.containsKey(key); + } + + public boolean containsValue(Object value) + { + return map.containsValue(value); + } + + /* + * @see org.alfresco.util.OneToManyMap#containsSingleValue(V) + */ + public boolean containsSingleValue(V value) + { + Collection> values = map.values(); + for (Set set : values) + { + if (set.contains(value)) return true; + + } + return false; + } + + public Set>> entrySet() + { + return map.entrySet(); + } + + /* + * @see org.alfresco.util.OneToManyMap#entries() + */ + public Set> entries() + { + Set> entries = new HashSet>(); + for (Entry> entry : map.entrySet()) + { + final K key = entry.getKey(); + final Set values = entry.getValue(); + for (final V value : values) + { + entries.add(new Entry() + { + + public K getKey() + { + return key; + } + + public V getValue() + { + return value; + } + + // Not Thread-safe! + public V setValue(V newValue) + { + throw new UnsupportedOperationException( + "Cannot modify the entries returned by " + + OneToManyHashMap.class.getName() + ".entries()!"); + } + }); + } + } + return entries; + } + + public Set get(Object key) + { + Set set = map.get(key); + if (set == null) set = new HashSet(); + return Collections.unmodifiableSet(set); + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public Set keySet() + { + return map.keySet(); + } + + public Set put(K key, Set value) + { + return map.put(key, value); + } + + /* + * @see org.alfresco.util.OneToManyMap#putSingleValue(K, V) + */ + public V putSingleValue(K key, V value) + { + Set values = map.get(key); + if (values == null) + { + values = new HashSet(); + map.put(key, values); + } + values.add(value); + return value; + } + + public void putAll(Map> m) + { + map.putAll(m); + } + + /* + * @see org.alfresco.util.OneToManyMap#putAllSingleValues(java.util.Map) + */ + public void putAllSingleValues(Map m) + { + for (Entry entry : m.entrySet()) + { + putSingleValue(entry.getKey(), entry.getValue()); + } + } + + public Set remove(Object key) + { + return map.remove(key); + } + + public int size() + { + return map.size(); + } + + public Collection> values() + { + return map.values(); + } + + /* + * @see org.alfresco.util.OneToManyMap#flatValues() + */ + public Collection flatValues() + { + LinkedList flatValues = new LinkedList(); + for (Set values : map.values()) + { + flatValues.addAll(values); + } + return flatValues; + } +} diff --git a/src/main/java/org/alfresco/util/OneToManyMap.java b/src/main/java/org/alfresco/util/OneToManyMap.java new file mode 100644 index 0000000000..e678f2c43b --- /dev/null +++ b/src/main/java/org/alfresco/util/OneToManyMap.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * An extension of java.util.Map that represents a mapping + * from a key to a set of values. In addition to the standard + * java.util.Map methods this interface also provides several useful + * methods for directly accessing the values rather than having to access values + * via a java.util.Set + * + * @author Nick Smith + */ +public interface OneToManyMap extends Map> +{ + /** + * This method returns true if any of the value sets in the + * OneToManyMap contains an equivalent object to the value + * parameter, where equivalence is determined using the + * equals(Object) method. + * + * @param value The value being searched for. + * @return Returns true if any of the value sets contains a + * matching value, otherwise returns false + */ + public abstract boolean containsSingleValue(V value); + + /** + * This method is similar to the java.util.Map.entrySet() + * method, however the entries returned map from a key to a value, rather + * than from a key(K) to a value(V) rather than + * froma key(K) to a set of values(Set<V>).
+ * Note that the entries returned by this method do not support the method + * java.util.Map.Entry.setValue(V). + * + * @return The + * Set<Entry<K, V>> representing all the key-value pairs in the ManyToOneMap. + */ + public abstract Set> entries(); + + /** + * This method is similar to the method java.util.Map.put(K, V) + * , however it allows the user to add a single value to the map rather than + * adding a java.util.Set containing one or more values. If the + * specified key already has a set of values associated with it then the new + * value is added to this set. Otherwise a new set is created and the new + * value is added to that. + * + * @param key + * @param value + * @return returns the newly added value. + */ + public abstract V putSingleValue(K key, V value); + + /** + * This method is similar to java.utilMap.putAll(Map m), + * however the map specified is from keys to values instead of keys to sets + * of values. + * + * @param m A map containing the key-value mappings to be added to the + * ManyToOneMap. + */ + public abstract void putAllSingleValues(Map m); + + /** + * Returns a Collection of all the values in the map. Unlike + * values() the values are in a single flattened + * Collection<V> rather than a + * Collection<Set<V>>. + * + * @return All the values in the map as a flattened Collection. + */ + public abstract Collection flatValues(); + +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/PackageMarker.java b/src/main/java/org/alfresco/util/PackageMarker.java new file mode 100644 index 0000000000..c851c7947b --- /dev/null +++ b/src/main/java/org/alfresco/util/PackageMarker.java @@ -0,0 +1,22 @@ +package org.alfresco.util; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This empty marker annotation is added to make sure .class files are actually generated + * for package-info.java files. This allow to speed up incremental compilation time, + * so that each build tool will be able to properly detect differences between sources and + * .class compiled files. + * + * See https://jira.codehaus.org/browse/MCOMPILER-205?focusedCommentId=326795&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-326795 + * for more details + * + * NOTE: This annotation should be added in each package-info.java file + * @author Gabriele Columbro + * + */ +@Retention(RetentionPolicy.SOURCE) +public @interface PackageMarker { + +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/Pair.java b/src/main/java/org/alfresco/util/Pair.java new file mode 100644 index 0000000000..60e5344692 --- /dev/null +++ b/src/main/java/org/alfresco/util/Pair.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.io.ObjectInputStream.GetField; + +import org.alfresco.api.AlfrescoPublicApi; + +/** + * Utility class for containing two things that aren't like each other + */ +@AlfrescoPublicApi +public final class Pair implements Serializable +{ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final Pair NULL_PAIR = new Pair(null, null); + + @SuppressWarnings("unchecked") + public static final Pair nullPair() + { + return NULL_PAIR; + } + + private static final long serialVersionUID = -7406248421185630612L; + + /** + * The first member of the pair. + */ + private F first; + + /** + * The second member of the pair. + */ + private S second; + + /** + * Make a new one. + * + * @param first The first member. + * @param second The second member. + */ + public Pair(F first, S second) + { + this.first = first; + this.second = second; + } + + /** + * Get the first member of the tuple. + * @return The first member. + */ + public final F getFirst() + { + return first; + } + + /** + * Get the second member of the tuple. + * @return The second member. + */ + public final S getSecond() + { + return second; + } + + public final void setFirst(F first) + { + this.first = first; + } + + public final void setSecond(S second) + { + this.second = second; + } + + @Override + public boolean equals(Object other) + { + if (this == other) + { + return true; + } + if (other == null || !(other instanceof Pair)) + { + return false; + } + Pair o = (Pair)other; + return EqualsHelper.nullSafeEquals(this.first, o.first) && + EqualsHelper.nullSafeEquals(this.second, o.second); + } + + @Override + public int hashCode() + { + return (first == null ? 0 : first.hashCode()) + (second == null ? 0 : second.hashCode()); + } + + @Override + public String toString() + { + return "(" + first + ", " + second + ")"; + } + + /** + * Ensure that previously-serialized instances don't fail due to the member name change. + */ + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException + { + GetField fields = is.readFields(); + if (fields.defaulted("first")) + { + // This is a pre-V3.3 + this.first = (F) fields.get("fFirst", null); + this.second = (S) fields.get("fSecond", null); + } + else + { + this.first = (F) fields.get("first", null); + this.second = (S) fields.get("second", null); + } + } +} diff --git a/src/main/java/org/alfresco/util/ParameterCheck.java b/src/main/java/org/alfresco/util/ParameterCheck.java new file mode 100644 index 0000000000..efc932e0b7 --- /dev/null +++ b/src/main/java/org/alfresco/util/ParameterCheck.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Collection; + +/** + * Utility class to perform various common parameter checks + * + * @author gavinc + */ +public final class ParameterCheck +{ + /** + * Checks that the parameter with the given name has content i.e. it is not + * null + * + * @param strParamName Name of parameter to check + * @param object Value of the parameter to check + */ + public static final void mandatory(final String strParamName, final Object object) + { + // check that the object is not null + if (object == null) + { + throw new IllegalArgumentException(strParamName + " is a mandatory parameter"); + } + } + + /** + * Checks that the string parameter with the given name has content i.e. it + * is not null and not zero length + * + * @param strParamName Name of parameter to check + * @param strParamValue Value of the parameter to check + */ + public static final void mandatoryString(final String strParamName, final String strParamValue) + { + // check that the given string value has content + if (strParamValue == null || strParamValue.length() == 0) + { + throw new IllegalArgumentException(strParamName + " is a mandatory parameter"); + } + } + + /** + * Checks that the collection parameter contains at least one item. + * + * @param strParamName Name of parameter to check + * @param coll collection to check + */ + public static final void mandatoryCollection(final String strParamName, final Collection coll) + { + if (coll == null || coll.size() == 0) + { + throw new IllegalArgumentException(strParamName + " collection must contain at least one item"); + } + } + +} diff --git a/src/main/java/org/alfresco/util/PathMapper.java b/src/main/java/org/alfresco/util/PathMapper.java new file mode 100644 index 0000000000..3b537ebcc4 --- /dev/null +++ b/src/main/java/org/alfresco/util/PathMapper.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A component that maps source data paths to target data paths. + *

+ * This class caches results and is thread-safe. + * + * @author Derek Hulley + * @since 3.2 + */ +public class PathMapper +{ + private static final Log logger = LogFactory.getLog(PathMapper.class); + + private final ReentrantReadWriteLock.ReadLock readLock; + private final ReentrantReadWriteLock.WriteLock writeLock; + + private boolean locked; + /** + * Used to lookup path translations + */ + private final Map> pathMaps; + /** + * Cached fine-grained path translations (derived data) + */ + private final Map> derivedPathMaps; + private final Map> derivedPathMapsPartial; + + /** + * Default constructor + */ + public PathMapper() + { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + readLock = lock.readLock(); + writeLock = lock.writeLock(); + + pathMaps = new HashMap>(37); + derivedPathMaps = new HashMap>(127); + derivedPathMapsPartial = new HashMap>(127); + } + + /** + * Locks the instance against further modifications. + */ + public void lock() + { + writeLock.lock(); + try + { + locked = true; + } + finally + { + writeLock.unlock(); + } + } + + public void clear() + { + writeLock.lock(); + try + { + if (locked) + { + throw new IllegalStateException("The PathMapper has been locked against further changes"); + } + pathMaps.clear(); + derivedPathMaps.clear(); + derivedPathMapsPartial.clear(); + } + finally + { + writeLock.unlock(); + } + } + + /** + * Add a path mapping. + * + * @param sourcePath the source path + * @param targetPath the target path + */ + public void addPathMap(String sourcePath, String targetPath) + { + writeLock.lock(); + try + { + if (locked) + { + throw new IllegalStateException("The PathMapper has been locked against further changes"); + } + derivedPathMaps.clear(); + derivedPathMapsPartial.clear(); + Set targetPaths = pathMaps.get(sourcePath); + if (targetPaths == null) + { + targetPaths = new HashSet(5); + pathMaps.put(sourcePath, targetPaths); + } + targetPaths.add(targetPath); + } + finally + { + writeLock.unlock(); + } + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Added path map: " + sourcePath + " --> " + targetPath); + } + } + + /** + * Gets the remapped paths for the given source path, excluding any derivative + * paths i.e. does exact path matching only. + * + * @param sourcePath the source path + * @return Returns the target paths (never null) + */ + public Set getMappedPaths(String sourcePath) + { + readLock.lock(); + try + { + Set targetPaths = derivedPathMaps.get(sourcePath); + if (targetPaths != null) + { + return targetPaths; + } + } + finally + { + readLock.unlock(); + } + // We didn't find anything, so update the cache + writeLock.lock(); + try + { + return updateMappedPaths(sourcePath); + } + finally + { + writeLock.unlock(); + } + } + + /** + * Gets the remapped paths for the given source path, including any derivative + * paths i.e. does partial path matching. + * + * @param sourcePath the source path + * @return Returns the target paths (never null) + */ + public Set getMappedPathsWithPartialMatch(String sourcePath) + { + readLock.lock(); + try + { + Set targetPaths = derivedPathMapsPartial.get(sourcePath); + if (targetPaths != null) + { + return targetPaths; + } + } + finally + { + readLock.unlock(); + } + // We didn't find anything, so update the cache + writeLock.lock(); + try + { + return updateMappedPathsPartial(sourcePath); + } + finally + { + writeLock.unlock(); + } + } + + public boolean isEmpty() + { + readLock.lock(); + try + { + return pathMaps.isEmpty(); + } + finally + { + readLock.unlock(); + } + } + + private Set updateMappedPaths(String sourcePath) + { + // Do a double-check + Set targetPaths = derivedPathMaps.get(sourcePath); + if (targetPaths != null) + { + return targetPaths; + } + targetPaths = new HashSet(17); + derivedPathMaps.put(sourcePath, targetPaths); + // Now remap it and build the target values + for (Map.Entry> entry : pathMaps.entrySet()) + { + String mapSourcePath = entry.getKey(); + Set mapTargetPaths = entry.getValue(); + // If the map source matches the source, then it's simple + if (mapSourcePath.equals(sourcePath)) + { + targetPaths.addAll(mapTargetPaths); + continue; + } + // It is not an exact match, so check if it starts with the source + int index = sourcePath.indexOf(mapSourcePath); + if (index != 0) + { + // It doesn't match the start, so ignore it + continue; + } + // Replace the beginning with the mapped targets + for (String mapTargetPath : mapTargetPaths) + { + if (mapTargetPath.equals(mapSourcePath)) + { + // Direct mapping, so shortcut + targetPaths.add(sourcePath); + } + else + { + String newPath = (mapTargetPath + sourcePath.substring(mapSourcePath.length())); + targetPaths.add(newPath); + } + } + } + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Cached path mapping: \n" + + " Source: " + sourcePath + "\n" + + " Targets: " + targetPaths); + } + return targetPaths; + } + + private Set updateMappedPathsPartial(String sourcePath) + { + // Do a double-check + Set targetPaths = derivedPathMapsPartial.get(sourcePath); + if (targetPaths != null) + { + return targetPaths; + } + targetPaths = new HashSet(17); + derivedPathMapsPartial.put(sourcePath, targetPaths); + // Now remap it and build the target values + for (Map.Entry> entry : pathMaps.entrySet()) + { + String mapSourcePath = entry.getKey(); + Set mapTargetPaths = entry.getValue(); + // It is not an exact match, so check if it starts with the source + int index = mapSourcePath.indexOf(sourcePath); + if (index != 0) + { + // It doesn't match the start, so ignore it + continue; + } + // Record the partial matches + targetPaths.addAll(mapTargetPaths); + } + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Cached path mapping (partial): \n" + + " Source: " + sourcePath + "\n" + + " Targets: " + targetPaths); + } + return targetPaths; + } + + public Map convertMap(Map valueMap) + { + Map resultMap = new HashMap(valueMap.size() * 2 + 1); + for (Map.Entry entry : valueMap.entrySet()) + { + String path = entry.getKey(); + V value = entry.getValue(); + Set mappedPaths = getMappedPaths(path); + for (String mappedPath : mappedPaths) + { + resultMap.put(mappedPath, value); + } + } + return resultMap; + } +} diff --git a/src/main/java/org/alfresco/util/PatternFilter.java b/src/main/java/org/alfresco/util/PatternFilter.java new file mode 100644 index 0000000000..1ec0a05c43 --- /dev/null +++ b/src/main/java/org/alfresco/util/PatternFilter.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . */ +package org.alfresco.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * Matches a path against a set of regular expression filters + * + */ +public class PatternFilter +{ + private List patterns; + + /** + * A list of regular expressions that represent patterns of files. + * + * @param regexps list of regular expressions + * + * @see String#matches(java.lang.String) + */ + public void setPatterns(List regexps) + { + this.patterns = new ArrayList(regexps.size()); + for(String regexp : regexps) + { + this.patterns.add(Pattern.compile(regexp)); + } + } + + public boolean isFiltered(String path) + { + // check against all the regular expressions + boolean matched = false; + + for (Pattern regexp : patterns) + { + if(!regexp.matcher(path).matches()) + { + // it is not a match - try next one + continue; + } + else + { + matched = true; + break; + } + } + + return matched; + } +} diff --git a/src/main/java/org/alfresco/util/PropertyCheck.java b/src/main/java/org/alfresco/util/PropertyCheck.java new file mode 100644 index 0000000000..37c4158a0b --- /dev/null +++ b/src/main/java/org/alfresco/util/PropertyCheck.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Helper class for for use when checking properties. This class uses + * I18N for its messages. + * + * @author Derek Hulley + */ +public class PropertyCheck +{ + public static final String ERR_PROPERTY_NOT_SET = "system.err.property_not_set"; + + /** + * Checks that the property with the given name is not null. + * + * @param target the object on which the property must have been set + * @param propertyName the name of the property + * @param value of the property value + */ + public static void mandatory(Object target, String propertyName, Object value) + { + if (value == null) + { + throw new AlfrescoRuntimeException( + ERR_PROPERTY_NOT_SET, + new Object[] {propertyName, target, target.getClass()}); + } + } + + /** + * Checks that the given string is not: + *

    + *
  • null
  • + *
  • empty
  • + *
  • a placeholder of form '${...}'
  • + *
+ * + * @param value the value to check + * @return true if the checks all pass + */ + public static boolean isValidPropertyString(String value) + { + if (value == null || value.length() == 0) + { + return false; + } + if (value.startsWith("${") && value.endsWith("}")) + { + return false; + } + else + { + return true; + } + } + + /** + * Dig out the property name from a placeholder-style property of form + * ${prop.name}, which will yield prop.name. If the placeholders + * are not there, the value is returned directly. null values are + * not allowed, but empty strings are. + * + * @param value The property with or without property placeholders + * @return Returns the core property without the property placeholders + * ${ and }. + * @throws IllegalArgumentException if the value is null + */ + public static String getPropertyName(String value) + { + if (value == null) + { + throw new IllegalArgumentException("'value' is a required argument."); + } + if (!value.startsWith("${")) + { + return value; + } + if (!value.endsWith("}")) + { + return value; + } + int strLen = value.length(); + return value.substring(2, strLen - 1); + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/ReadWriteLockExecuter.java b/src/main/java/org/alfresco/util/ReadWriteLockExecuter.java new file mode 100644 index 0000000000..1b9a9928da --- /dev/null +++ b/src/main/java/org/alfresco/util/ReadWriteLockExecuter.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Utility object that wraps read and write methods within the context of a + * {@link ReentrantReadWriteLock}. The callback's methods are best-suited + * to fetching values from a cache or protecting members that need lazy + * initialization. + *

+ * Client code should construct an instance of this class for each resource + * (or set of resources) that need to be protected. + * + * @author Derek Hulley + * @since 3.4 + */ +public abstract class ReadWriteLockExecuter +{ + private ReentrantReadWriteLock.ReadLock readLock; + private ReentrantReadWriteLock.WriteLock writeLock; + + /** + * Default constructor + */ + public ReadWriteLockExecuter() + { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + readLock = lock.readLock(); + writeLock = lock.writeLock(); + } + + /** + * Execute the read-only part of the work. + * + * @return Returns a value of interest or null if + * the {@link #getWithWriteLock()} method must be + * called + * @throws Throwable all checked exceptions are wrapped in a RuntimeException + */ + protected abstract T getWithReadLock() throws Throwable; + + /** + * Execute the write part of the work. + *

+ * NOTE: It is important to perform a double-check on the resource + * before assuming it is not null; there is a window between the {@link #getWithReadLock()} + * and the {@link #getWithWriteLock()} during which another thread may have populated + * the resource of interest. + * + * @return Returns the value of interest of null + * @throws Throwable all checked exceptions are wrapped in a RuntimeException + */ + protected abstract T getWithWriteLock() throws Throwable; + + public T execute() + { + T ret = null; + readLock.lock(); + try + { + ret = this.getWithReadLock(); + // We do the null check here so that less time is spent outside of the lock + if (ret != null) + { + return ret; + } + } + catch (RuntimeException e) + { + throw e; + } + catch (Throwable e) + { + throw new RuntimeException("Exception during 'getWithReadLock'", e); + } + finally + { + readLock.unlock(); + } + // If we got here, then we didn't get a result and need to go for the write lock + writeLock.lock(); + try + { + // The return value is not of interest to us + return this.getWithWriteLock(); + } + catch (RuntimeException e) + { + throw e; + } + catch (Throwable e) + { + throw new RuntimeException("Exception during 'getWithWriteLock'", e); + } + finally + { + writeLock.unlock(); + } + } +} diff --git a/src/main/java/org/alfresco/util/ReflectionHelper.java b/src/main/java/org/alfresco/util/ReflectionHelper.java new file mode 100644 index 0000000000..6d06ea326b --- /dev/null +++ b/src/main/java/org/alfresco/util/ReflectionHelper.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Static Helper methods for instantiating objects from reflection. + * + * @author muzquiano + */ +public class ReflectionHelper +{ + private static Log logger = LogFactory.getLog(ReflectionHelper.class); + + private ReflectionHelper() + { + } + + /** + * Constructs a new object for the given class name. + * The construction takes no arguments. + * + * If an exception occurs during construction, null is returned. + * + * All exceptions are written to the Log instance for this class. + * + * @param className String + * @return Object + */ + public static Object newObject(String className) + { + Object o = null; + + try + { + Class clazz = Class.forName(className); + o = clazz.newInstance(); + } + catch (ClassNotFoundException cnfe) + { + logger.debug(cnfe); + } + catch (InstantiationException ie) + { + logger.debug(ie); + } + catch (IllegalAccessException iae) + { + logger.debug(iae); + } + return o; + } + + /** + * Constructs a new object for the given class name and with the given + * arguments. The arguments must be specified in terms of their Class[] + * types and their Object[] values. + * + * Example: + * + * String s = newObject("java.lang.String", new Class[] { String.class}, + * new String[] { "test"}); + * + * is equivalent to: + * + * String s = new String("test"); + * + * If an exception occurs during construction, null is returned. + * + * All exceptions are written to the Log instance for this class. + + * @param className String + * @param argTypes Class[] + * @param args Object[] + * @return Object + */ + public static Object newObject(String className, Class[] argTypes, Object[] args) + { + /** + * We have some mercy here - if they called and did not pass in any + * arguments, then we will call through to the pure newObject() method. + */ + if (args == null || args.length == 0) + { + return newObject(className); + } + + /** + * Try to build the object + * + * If an exception occurs, we log it and return null. + */ + Object o = null; + try + { + // base class + Class clazz = Class.forName(className); + + Constructor c = clazz.getDeclaredConstructor(argTypes); + o = c.newInstance(args); + } + catch (ClassNotFoundException cnfe) + { + logger.debug(cnfe); + } + catch (InstantiationException ie) + { + logger.debug(ie); + } + catch (IllegalAccessException iae) + { + logger.debug(iae); + } + catch (NoSuchMethodException nsme) + { + logger.debug(nsme); + } + catch (InvocationTargetException ite) + { + logger.debug(ite); + } + return o; + } + + /** + * Invokes a method on the given object by passing the given arguments + * into the method. + * + * @param obj Object + * @param method String + * @param argTypes Class[] + * @param args Object[] + * @return Object + */ + public static Object invoke(Object obj, String method, Class[] argTypes, Object[] args) + { + if (obj == null || method == null) + { + throw new IllegalArgumentException("Object and Method must be supplied."); + } + + /** + * Try to invoke the method. + * + * If the method is unable to be invoked, we log and return null. + */ + try + { + Method m = obj.getClass().getMethod(method, argTypes); + if(m != null) + { + return m.invoke(obj, args); + } + } + catch(NoSuchMethodException nsme) + { + logger.debug(nsme); + } + catch(IllegalAccessException iae) + { + logger.debug(iae); + } + catch(InvocationTargetException ite) + { + logger.debug(ite); + } + + return null; + } +} diff --git a/src/main/java/org/alfresco/util/SchedulerStarterBean.java b/src/main/java/org/alfresco/util/SchedulerStarterBean.java new file mode 100644 index 0000000000..d236e15fe6 --- /dev/null +++ b/src/main/java/org/alfresco/util/SchedulerStarterBean.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; + +public class SchedulerStarterBean extends AbstractLifecycleBean +{ + protected final static Log log = LogFactory.getLog(SchedulerStarterBean.class); + + private Scheduler scheduler; + + @Override + protected void onBootstrap(ApplicationEvent event) + { + try + { + log.info("Scheduler started"); + scheduler.start(); + } + catch (SchedulerException e) + { + throw new AlfrescoRuntimeException("Scheduler failed to start", e); + } + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // Nothing required + // This is done by the SchedulerFactoryBean.destroy() - DisposableBean + } + + public Scheduler getScheduler() + { + return scheduler; + } + + public void setScheduler(Scheduler scheduler) + { + this.scheduler = scheduler; + } + +} diff --git a/src/main/java/org/alfresco/util/SerializationUtils.java b/src/main/java/org/alfresco/util/SerializationUtils.java new file mode 100644 index 0000000000..0b58e62d74 --- /dev/null +++ b/src/main/java/org/alfresco/util/SerializationUtils.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * NOTE: This utility class is a copy of org.apache.commons.lang3.SerializationUtils + * + * Please see http://issues.alfresco.com/jira/browse/ALF-5044 for why this is done. + * + * @author Apache Software Foundation + * @author Daniel L. Rall + * @author Jeff Varszegi + * @author Gary Gregory + * + *

+ * Assists with the serialization process and performs additional functionality + * based on serialization. + *

+ *

+ *

    + *
  • Deep clone using serialization + *
  • Serialize managing finally and IOException + *
  • Deserialize managing finally and IOException + *
+ * + *

+ * This class throws exceptions for invalid null inputs. Each + * method documents its behaviour in more detail. + *

+ * + *

+ * #ThreadSafe# + *

+ * + */ +public class SerializationUtils +{ + + /** + *

+ * SerializationUtils instances should NOT be constructed in standard + * programming. Instead, the class should be used as + * SerializationUtils.clone(object). + *

+ * + *

+ * This constructor is public to permit tools that require a JavaBean + * instance to operate. + *

+ */ + public SerializationUtils() + { + super(); + } + + // Clone + // ----------------------------------------------------------------------- + /** + *

+ * Deep clone an Object using serialization. + *

+ * + *

+ * This is many times slower than writing clone methods by hand on all + * objects in your object graph. However, for complex object graphs, or for + * those that don't support deep cloning this can be a simple alternative + * implementation. Of course all the objects must be + * Serializable. + *

+ * + * @param object + * the Serializable object to clone + * @return the cloned object + * @throws AlfrescoRuntimeException + * (runtime) if the serialization fails + */ + public static T clone(T object) + { + /* + * when we serialize and deserialize an object, it is reasonable to + * assume the deserialized object is of the same type as the original + * serialized object + */ + @SuppressWarnings("unchecked") + final T result = (T) deserialize(serialize(object)); + return result; + } + + // Serialize + // ----------------------------------------------------------------------- + /** + *

+ * Serializes an Object to the specified stream. + *

+ * + *

+ * The stream will be closed once the object is written. This avoids the + * need for a finally clause, and maybe also exception handling, in the + * application code. + *

+ * + *

+ * The stream passed in is not buffered internally within this method. This + * is the responsibility of your application if desired. + *

+ * + * @param obj + * the object to serialize to bytes, may be null + * @param outputStream + * the stream to write to, must not be null + * @throws IllegalArgumentException + * if outputStream is null + * @throws AlfrescoRuntimeException + * (runtime) if the serialization fails + */ + public static void serialize(Serializable obj, OutputStream outputStream) + { + if (outputStream == null) + { + throw new IllegalArgumentException("The OutputStream must not be null"); + } + ObjectOutputStream out = null; + try + { + // stream closed in the finally + out = new ObjectOutputStream(outputStream); + out.writeObject(obj); + + } catch (IOException ex) + { + throw new AlfrescoRuntimeException("Failed to serialize", ex); + } finally + { + try + { + if (out != null) + { + out.close(); + } + } catch (IOException ex) + { + // ignore close exception + } + } + } + + /** + *

+ * Serializes an Object to a byte array for + * storage/serialization. + *

+ * + * @param obj + * the object to serialize to bytes + * @return a byte[] with the converted Serializable + * @throws AlfrescoRuntimeException + * (runtime) if the serialization fails + */ + public static byte[] serialize(Serializable obj) + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(512); + serialize(obj, baos); + return baos.toByteArray(); + } + + // Deserialize + // ----------------------------------------------------------------------- + /** + *

+ * Deserializes an Object from the specified stream. + *

+ * + *

+ * The stream will be closed once the object is written. This avoids the + * need for a finally clause, and maybe also exception handling, in the + * application code. + *

+ * + *

+ * The stream passed in is not buffered internally within this method. This + * is the responsibility of your application if desired. + *

+ * + * @param inputStream + * the serialized object input stream, must not be null + * @return the deserialized object + * @throws IllegalArgumentException + * if inputStream is null + * @throws AlfrescoRuntimeException + * (runtime) if the serialization fails + */ + public static Object deserialize(InputStream inputStream) + { + if (inputStream == null) + { + throw new IllegalArgumentException("The InputStream must not be null"); + } + ObjectInputStream in = null; + try + { + // stream closed in the finally + in = new ObjectInputStream(inputStream); + return in.readObject(); + + } catch (ClassNotFoundException ex) + { + throw new AlfrescoRuntimeException("Failed to deserialize", ex); + } catch (IOException ex) + { + throw new AlfrescoRuntimeException("Failed to deserialize", ex); + } finally + { + try + { + if (in != null) + { + in.close(); + } + } catch (IOException ex) + { + // ignore close exception + } + } + } + + /** + *

+ * Deserializes a single Object from an array of bytes. + *

+ * + * @param objectData + * the serialized object, must not be null + * @return the deserialized object + * @throws IllegalArgumentException + * if objectData is null + * @throws AlfrescoRuntimeException + * (runtime) if the serialization fails + */ + public static Object deserialize(byte[] objectData) + { + if (objectData == null) + { + throw new IllegalArgumentException("The byte[] must not be null"); + } + ByteArrayInputStream bais = new ByteArrayInputStream(objectData); + return deserialize(bais); + } + +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/TempFileProvider.java b/src/main/java/org/alfresco/util/TempFileProvider.java new file mode 100644 index 0000000000..678515de40 --- /dev/null +++ b/src/main/java/org/alfresco/util/TempFileProvider.java @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * A helper class that provides temporary files, providing a common point to clean + * them up. + * + *

+ * The contents of ALFRESCO_TEMP_FILE_DIR [%java.io.tmpdir%/Alfresco] are managed by this + * class. Temporary files and directories are cleaned by TempFileCleanerJob so that + * after a delay [default 1 hour] the contents of the alfresco temp dir, + * both files and directories are removed. + * + *

+ * Some temporary files may need to live longer than 1 hour. The temp file provider allows special sub folders which + * are cleaned less frequently. By default, files in the long life folders will remain for 24 hours + * unless cleaned by the application code earlier. + * + *

+ * The other contents of %java.io.tmpdir% are not touched by the cleaner job. + * + *

TempFileCleanerJob Job Data: protectHours, number of hours to keep temporary files, default 1 hour. + * + * @author derekh + * @author mrogers + */ +@AlfrescoPublicApi +public class TempFileProvider +{ + private static final int BUFFER_SIZE = 40 * 1024; + + /** + * subdirectory in the temp directory where Alfresco temporary files will go + */ + public static final String ALFRESCO_TEMP_FILE_DIR = "Alfresco"; + + /** + * The prefix for the long life temporary files. + */ + public static final String ALFRESCO_LONG_LIFE_FILE_DIR = "longLife"; + + /** the system property key giving us the location of the temp directory */ + public static final String SYSTEM_KEY_TEMP_DIR = "java.io.tmpdir"; + + private static final Log logger = LogFactory.getLog(TempFileProvider.class); + + private static int MAX_RETRIES = 3; + + /** + * Static class only + */ + private TempFileProvider() + { + } + + /** + * Get the Java Temp dir e.g. java.io.tempdir + * + * @return Returns the system temporary directory i.e. isDir == true + */ + public static File getSystemTempDir() + { + String systemTempDirPath = System.getProperty(SYSTEM_KEY_TEMP_DIR); + if (systemTempDirPath == null) + { + throw new AlfrescoRuntimeException("System property not available: " + SYSTEM_KEY_TEMP_DIR); + } + File systemTempDir = new File(systemTempDirPath); + if (logger.isDebugEnabled()) + { + logger.debug("Created system temporary directory: " + systemTempDir); + } + return systemTempDir; + } + + /** + * Get the Alfresco temp dir, by defaut %java.io.tempdir%/Alfresco. + * Will create the temp dir on the fly if it does not already exist. + * + * @return Returns a temporary directory, i.e. isDir == true + */ + public static File getTempDir() + { + return getTempDir(ALFRESCO_TEMP_FILE_DIR); + } + + /** + * Get the specified temp dir, %java.io.tempdir%/dirName. + * Will create the temp dir on the fly if it does not already exist. + * + * @param dirName the name of sub-directory in %java.io.tempdir% + * + * @return Returns a temporary directory, i.e. isDir == true + */ + public static File getTempDir(String dirName) + { + File systemTempDir = getSystemTempDir(); + // append the Alfresco directory + File tempDir = new File(systemTempDir, dirName); + // ensure that the temp directory exists + if (tempDir.exists()) + { + // nothing to do + } + else + { + // not there yet + if (!tempDir.mkdirs()) + { + // We didn't create it but perhaps it was made by some other thread + if (!tempDir.exists()) + { + // It's definitely not there + throw new AlfrescoRuntimeException("Failed to create temp directory: " + tempDir); + } + } + else + { + // This thread created it + if (logger.isDebugEnabled()) + { + logger.debug("Created temp directory: " + tempDir); + } + } + } + // done + return tempDir; + } + + /** + * creates a longer living temp dir. Files within the longer living + * temp dir will not be garbage collected as soon as "normal" temporary files. + * By default long life temp files will live for for 24 hours rather than 1 hour. + *

+ * Code using the longer life temporary files should be careful to clean up since + * abuse of this feature may result in out of memory/disk space errors. + * @param key can be blank in which case the system will generate a folder to be used by all processes + * or can be used to create a unique temporary folder name for a particular process. At the end of the process + * the client can simply delete the entire temporary folder. + * @return the long life temporary directory + */ + public static File getLongLifeTempDir(String key) + { + /** + * Long life temporary directories have a prefix at the start of the + * folder name. + */ + String folderName = ALFRESCO_LONG_LIFE_FILE_DIR + "_" + key; + + File tempDir = getTempDir(); + + // append the Alfresco directory + File longLifeDir = new File(tempDir, folderName); + // ensure that the temp directory exists + + if (longLifeDir.exists()) + { + if (logger.isDebugEnabled()) + { + logger.debug("Already exists: " + longLifeDir); + } + // nothing to do + return longLifeDir; + } + else + { + /** + * We need to create a temporary directory + * + * We may have a race condition here if more than one thread attempts to create + * the temp dir. + * + * mkdirs can't be synchronized + * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4742723 + */ + for(int retry = 0; retry < MAX_RETRIES; retry++) + { + boolean created = longLifeDir.mkdirs(); + + if (created) + { + // Yes we created the temp dir + if (logger.isDebugEnabled()) + { + logger.debug("Created long life temp directory: " + longLifeDir); + } + return longLifeDir; + } + else + { + if(longLifeDir.exists()) + { + // created by another thread, but that's O.K. + if (logger.isDebugEnabled()) + { + logger.debug("Another thread created long life temp directory: " + longLifeDir); + } + return longLifeDir; + } + } + } + } + throw new AlfrescoRuntimeException("Failed to create temp directory: " + longLifeDir); + } + + public static File createTempFile(InputStream in, String namePrefix, String nameSufix) throws Exception + { + if (null == in) + { + return null; + } + + File file = createTempFile(namePrefix, nameSufix); + OutputStream out = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE); + try + { + byte[] buffer = new byte[BUFFER_SIZE]; + int i; + while ((i = in.read(buffer)) > -1) + { + out.write(buffer, 0, i); + } + } + catch (Exception e) + { + file.delete(); + throw e; + } + finally + { + in.close(); + out.flush(); + out.close(); + } + + return file; + } + + /** + * Is this a long life folder ? + * @param file + * @return true, this is a long life folder. + */ + private static boolean isLongLifeTempDir(File file) + { + if(file.isDirectory()) + { + if(file.getName().startsWith(ALFRESCO_LONG_LIFE_FILE_DIR)) + { + return true; + } + else + { + return false; + } + } + return false; + } + + /** + * Create a temp file in the alfresco temp dir. + * + * @return Returns a temp File that will be located in the + * Alfresco subdirectory of the default temp directory + * + * @see #ALFRESCO_TEMP_FILE_DIR + * @see File#createTempFile(java.lang.String, java.lang.String) + */ + public static File createTempFile(String prefix, String suffix) + { + File tempDir = TempFileProvider.getTempDir(); + // we have the directory we want to use + return createTempFile(prefix, suffix, tempDir); + } + + /** + * @return Returns a temp File that will be located in the + * given directory + * + * @see #ALFRESCO_TEMP_FILE_DIR + * @see File#createTempFile(java.lang.String, java.lang.String) + */ + public static File createTempFile(String prefix, String suffix, File directory) + { + try + { + File tempFile = File.createTempFile(prefix, suffix, directory); + if (logger.isDebugEnabled()) + { + logger.debug("Creating tmp file: " + tempFile); + } + return tempFile; + } catch (IOException e) + { + throw new AlfrescoRuntimeException("Failed to created temp file: \n" + + " prefix: " + prefix + "\n" + + " suffix: " + suffix + "\n" + + " directory: " + directory, + e); + } + } + + /** + * Cleans up all Alfresco temporary files that are older than the + * given number of hours. Subdirectories are emptied as well and all directories + * below the primary temporary subdirectory are removed. + *

+ * The job data must include a property protectHours, which is the + * number of hours to protect a temporary file from deletion since its last + * modification. + * + * @author Derek Hulley + */ + @AlfrescoPublicApi + public static class TempFileCleanerJob implements Job + { + public static final String KEY_PROTECT_HOURS = "protectHours"; + public static final String KEY_DIRECTORY_NAME = "directoryName"; + + /** + * Gets a list of all files in the {@link TempFileProvider#ALFRESCO_TEMP_FILE_DIR temp directory} + * and deletes all those that are older than the given number of hours. + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + // get the number of hours to protect the temp files + String strProtectHours = (String) context.getJobDetail().getJobDataMap().get(KEY_PROTECT_HOURS); + if (strProtectHours == null) + { + throw new JobExecutionException("Missing job data: " + KEY_PROTECT_HOURS); + } + int protectHours = -1; + try + { + protectHours = Integer.parseInt(strProtectHours); + } + catch (NumberFormatException e) + { + throw new JobExecutionException("Invalid job data " + KEY_PROTECT_HOURS + ": " + strProtectHours); + } + if (protectHours < 0 || protectHours > 8760) + { + throw new JobExecutionException("Hours to protect temp files must be 0 <= x <= 8760"); + } + + String directoryName = (String) context.getJobDetail().getJobDataMap().get(KEY_DIRECTORY_NAME); + + if (directoryName == null) + { + directoryName = ALFRESCO_TEMP_FILE_DIR; + } + + long now = System.currentTimeMillis(); + long aFewHoursBack = now - (3600L * 1000L * protectHours); + + long aLongTimeBack = now - (24 * 3600L * 1000L); + + File tempDir = TempFileProvider.getTempDir(directoryName); + int count = removeFiles(tempDir, aFewHoursBack, aLongTimeBack, false); // don't delete this directory + // done + if (logger.isDebugEnabled()) + { + logger.debug("Removed " + count + " files from temp directory: " + tempDir); + } + } + + /** + * Removes all temporary files created before the given time. + *

+ * The delete will cascade down through directories as well. + * + * @param removeBefore only remove files created before this time + * @return Returns the number of files removed + */ + public static int removeFiles(long removeBefore) + { + File tempDir = TempFileProvider.getTempDir(); + return removeFiles(tempDir, removeBefore, removeBefore, false); + } + + /** + * @param directory the directory to clean out - the directory will optionally be removed + * @param removeBefore only remove files created before this time + * @param removeDir true if the directory must be removed as well, otherwise false + * @return Returns the number of files removed + */ + private static int removeFiles(File directory, long removeBefore, long longLifeBefore, boolean removeDir) + { + if (!directory.isDirectory()) + { + throw new IllegalArgumentException("Expected a directory to clear: " + directory); + } + // check if there is anything to to + if (!directory.exists()) + { + return 0; + } + // list all files + File[] files = directory.listFiles(); + int count = 0; + for (File file : files) + { + if (file.isDirectory()) + { + if(isLongLifeTempDir(file)) + { + // long life for this folder and its children + int countRemoved = removeFiles(file, longLifeBefore, longLifeBefore, true); + if (logger.isDebugEnabled()) + { + logger.debug("Removed " + countRemoved + " files from temp directory: " + file); + } + } + else + { + // enter subdirectory and clean it out and remove itsynetics + int countRemoved = removeFiles(file, removeBefore, longLifeBefore, true); + if (logger.isDebugEnabled()) + { + logger.debug("Removed " + countRemoved + " files from directory: " + file); + } + } + } + else + { + // it is a file - check the created time + if (file.lastModified() > removeBefore) + { + // file is not old enough + continue; + } + // it is a file - attempt a delete + try + { + if(logger.isDebugEnabled()) + { + logger.debug("Deleting temp file: " + file); + } + file.delete(); + count++; + } + catch (Throwable e) + { + logger.info("Failed to remove temp file: " + file); + } + } + } + // must we delete the directory we are in? + if (removeDir) + { + // the directory must be removed if empty + try + { + File[] listing = directory.listFiles(); + if(listing != null && listing.length == 0) + { + // directory is empty + if(logger.isDebugEnabled()) + { + logger.debug("Deleting empty directory: " + directory); + } + directory.delete(); + } + } + catch (Throwable e) + { + logger.info("Failed to remove temp directory: " + directory, e); + } + } + // done + return count; + } + } +} diff --git a/src/main/java/org/alfresco/util/TraceableThreadFactory.java b/src/main/java/org/alfresco/util/TraceableThreadFactory.java new file mode 100644 index 0000000000..b59900b1c6 --- /dev/null +++ b/src/main/java/org/alfresco/util/TraceableThreadFactory.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A thread factory that spawns threads that are statically visible. Each factory uses a unique + * thread group. All the groups that have been used can be fetched using + * {@link #getActiveThreadGroups()}, allowing iteration of the the threads in the group. + * + * @since 2.1 + * @author Derek Hulley + */ +public class TraceableThreadFactory implements ThreadFactory +{ + private static final AtomicInteger factoryNumber = new AtomicInteger(1); + private static List activeThreadGroups = Collections.synchronizedList(new ArrayList(1)); + + /** + * Get a list of thread groups registered by the factory. + * + * @return Returns a snapshot of thread groups + */ + public static List getActiveThreadGroups() + { + return activeThreadGroups; + } + + private final ThreadGroup group; + private String namePrefix; + private final AtomicInteger threadNumber; + private boolean threadDaemon; + private int threadPriority; + + + public TraceableThreadFactory() + { + this.group = new ThreadGroup("TraceableThreadGroup-" + factoryNumber.getAndIncrement()); + TraceableThreadFactory.activeThreadGroups.add(this.group); + + this.namePrefix = "TraceableThread-" + factoryNumber.getAndIncrement() + "-thread-"; + this.threadNumber = new AtomicInteger(1); + + this.threadDaemon = true; + this.threadPriority = Thread.NORM_PRIORITY; + } + + /** + * @param daemon true if all threads created must be daemon threads + */ + public void setThreadDaemon(boolean daemon) + { + this.threadDaemon = daemon; + } + + /** + * + * @param threadPriority the threads priority from 1 (lowest) to 10 (highest) + */ + public void setThreadPriority(int threadPriority) + { + this.threadPriority = threadPriority; + } + + public Thread newThread(Runnable r) + { + Thread thread = new Thread( + group, + r, + namePrefix + threadNumber.getAndIncrement(), + 0); + thread.setDaemon(threadDaemon); + thread.setPriority(threadPriority); + + return thread; + } + + public void setNamePrefix(String namePrefix) + { + this.namePrefix = namePrefix; + } + + public String getNamePrefix() + { + return this.namePrefix; + } + +} diff --git a/src/main/java/org/alfresco/util/TriggerBean.java b/src/main/java/org/alfresco/util/TriggerBean.java new file mode 100644 index 0000000000..620e6a2aee --- /dev/null +++ b/src/main/java/org/alfresco/util/TriggerBean.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Date; + +import org.quartz.Scheduler; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; + +public class TriggerBean extends AbstractTriggerBean implements TriggerBeanSPI +{ + public long startDelay = 0; + + public long repeatInterval = 0; + + public int repeatCount = SimpleTrigger.REPEAT_INDEFINITELY; + + public TriggerBean() + { + super(); + } + + @Override + public int getRepeatCount() + { + return repeatCount; + } + + @Override + public void setRepeatCount(int repeatCount) + { + this.repeatCount = repeatCount; + } + + @Override + public long getRepeatInterval() + { + return repeatInterval; + } + + @Override + public void setRepeatInterval(long repeatInterval) + { + this.repeatInterval = repeatInterval; + } + + @Override + public void setRepeatIntervalMinutes(long repeatIntervalMinutes) + { + this.repeatInterval = repeatIntervalMinutes * 60L * 1000L; + } + + @Override + public long getStartDelay() + { + return startDelay; + } + + @Override + public void setStartDelay(long startDelay) + { + this.startDelay = startDelay; + } + + @Override + public void setStartDelayMinutes(long startDelayMinutes) + { + this.startDelay = startDelayMinutes * 60L * 1000L; + } + + @Override + public Trigger getTrigger() throws Exception + { + if ((repeatInterval <= 0) && (repeatCount != 0)) + { + logger.error("Job "+getBeanName()+" - repeatInterval/repeatIntervalMinutes cannot be 0 (or -ve) unless repeatCount is also 0"); + return null; + } + + SimpleTrigger trigger = new SimpleTrigger(getBeanName(), Scheduler.DEFAULT_GROUP); + trigger.setStartTime(new Date(System.currentTimeMillis() + this.startDelay)); + trigger.setRepeatCount(repeatCount); + trigger.setRepeatInterval(repeatInterval); + return trigger; + } +} diff --git a/src/main/java/org/alfresco/util/TriggerBeanSPI.java b/src/main/java/org/alfresco/util/TriggerBeanSPI.java new file mode 100644 index 0000000000..e4c5d662fc --- /dev/null +++ b/src/main/java/org/alfresco/util/TriggerBeanSPI.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +/** + * @author Andy + * + */ +public interface TriggerBeanSPI +{ + + /** + * @return int + */ + int getRepeatCount(); + + /** + * @param repeatCount int + */ + void setRepeatCount(int repeatCount); + + /** + * @return long + */ + long getRepeatInterval(); + + /** + * @param repeatInterval long + */ + void setRepeatInterval(long repeatInterval); + + /** + * @param repeatIntervalMinutes long + */ + void setRepeatIntervalMinutes(long repeatIntervalMinutes); + + /** + * @return long + */ + long getStartDelay(); + + /** + * @param startDelay long + */ + void setStartDelay(long startDelay); + + /** + * @param startDelayMinutes long + */ + void setStartDelayMinutes(long startDelayMinutes); + +} diff --git a/src/main/java/org/alfresco/util/Triple.java b/src/main/java/org/alfresco/util/Triple.java new file mode 100644 index 0000000000..24785f0191 --- /dev/null +++ b/src/main/java/org/alfresco/util/Triple.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.util; + +/** + * Utility class for containing three things that aren't like each other. + * + * @since 4.0 + */ +public final class Triple +{ + /** + * The first member of the triple. + */ + private final T first; + + /** + * The second member of the triple. + */ + private final U second; + + /** + * The third member of the triple. + */ + private final V third; + + /** + * Make a new one. + * + * @param first The first member. + * @param second The second member. + * @param third The third member. + */ + public Triple(final T first, final U second, final V third) + { + this.first = first; + this.second = second; + this.third = third; + } + + /** + * Get the first member of the tuple. + * @return The first member. + */ + public T getFirst() + { + return first; + } + + /** + * Get the second member of the tuple. + * @return The second member. + */ + public U getSecond() + { + return second; + } + + /** + * Get the third member of the tuple. + * @return The third member. + */ + public V getThird() + { + return third; + } + + /** + * Override of equals. + * @param other The thing to compare to. + * @return equality. + */ + public boolean equals(final Object other) + { + if (this == other) + { + return true; + } + + if (!(other instanceof Triple)) + { + return false; + } + + Triple o = (Triple)other; + return (first.equals(o.getFirst()) && + second.equals(o.getSecond()) && + third.equals(o.getThird())); + } + + /** + * Override of hashCode. + */ + public int hashCode() + { + return ((first == null ? 0 : first.hashCode()) + + (second == null ? 0 : second.hashCode()) + + (third == null ? 0 : third.hashCode())); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() + { + return "(" + first + ", " + second + ", " + third + ")"; + } +} diff --git a/src/main/java/org/alfresco/util/VersionNumber.java b/src/main/java/org/alfresco/util/VersionNumber.java new file mode 100644 index 0000000000..2c18bfba54 --- /dev/null +++ b/src/main/java/org/alfresco/util/VersionNumber.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.Serializable; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Immutable class to encapsulate a version number string. + * + * A valid version number string can be made up of any number of numberical parts + * all delimited by '.'. + * + * @author Roy Wetherall + */ +@AlfrescoPublicApi +public final class VersionNumber implements Comparable, Serializable +{ + private static final long serialVersionUID = -1570247769786810251L; + + /** A convenient '0' version */ + public static final VersionNumber VERSION_ZERO = new VersionNumber("0"); + /** A convenient '999' version */ + public static final VersionNumber VERSION_BIG = new VersionNumber("999"); + + /** Version delimeter */ + private static final String DELIMITER = "\\."; + + /** Version parts */ + private final int[] parts; + + /** + * Constructror, expects a valid version string. + * + * A AlfrescoRuntimeException will be throw if an invalid version is encountered. + * + * @param version the version string + */ + public VersionNumber(String version) + { + // Split the version into its component parts + String[] versions = version.split(DELIMITER); + if (versions.length < 1) + { + throw new AlfrescoRuntimeException("The version string '" + version + "' is invalid."); + } + + try + { + // Set the parts of the version + int index = 0; + this.parts = new int[versions.length]; + for (String versionPart : versions) + { + int part = Integer.parseInt(versionPart); + this.parts[index] = part; + index++; + } + } + catch (NumberFormatException e) + { + throw new AlfrescoRuntimeException("The version string '" + version + "' is invalid."); + } + } + + /** + * Get the various parts of the version + * + * @return array containing the parts of the version + */ + public int[] getParts() + { + return this.parts.clone(); + } + + /** + * Compares the passed version to this. Determines whether they are equal, greater or less than this version. + * + * @param obj the other version number + * @return -1 if the passed version is less that this, 0 if they are equal, 1 if the passed version is greater + */ + public int compareTo(VersionNumber obj) + { + int result = 0; + + VersionNumber that = (VersionNumber)obj; + int length = 0; + if (this.parts.length > that.parts.length) + { + length = this.parts.length; + } + else + { + length = that.parts.length; + } + + for (int index = 0; index < length; index++) + { + int thisPart = this.getPart(index); + int thatPart = that.getPart(index); + + if (thisPart > thatPart) + { + result = 1; + break; + } + else if (thisPart < thatPart) + { + result = -1; + break; + } + } + + return result; + } + + /** + * Helper method to the the part based on the index, if an invalid index is supplied 0 is returned. + * + * @param index the index + * @return the part value, 0 if the index is invalid + */ + public int getPart(int index) + { + int result = 0; + if (index < this.parts.length) + { + result = this.parts[index]; + } + return result; + } + + /** + * Hash code implementation + */ + @Override + public int hashCode() + { + if (parts == null || parts.length == 0) + { + return 0; + } + else if (parts.length >= 2) + { + return parts[0] * 17 + parts[1]; + } + else + { + return parts[0]; + } + } + + /** + * Equals implementation + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (false == obj instanceof VersionNumber) + { + return false; + } + VersionNumber that = (VersionNumber) obj; + return this.compareTo(that) == 0; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (int part : parts) + { + if (!first) + { + sb.append("."); + } + first = false; + sb.append(part); + } + return sb.toString(); + } +} diff --git a/src/main/java/org/alfresco/util/VmShutdownListener.java b/src/main/java/org/alfresco/util/VmShutdownListener.java new file mode 100644 index 0000000000..8682b93c31 --- /dev/null +++ b/src/main/java/org/alfresco/util/VmShutdownListener.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A class that keeps track of the VM shutdown status. It can be + * used by threads as a singleton to check if the + * VM shutdown status has been activated. + *

+ * NOTE: In order to prevent a proliferation of shutdown hooks, + * it is advisable to use instances as singletons only. + *

+ * This component should be used by long-running, but interruptable processes. + * + * @author Derek Hulley + */ +public class VmShutdownListener +{ + private Log logger; + private volatile boolean vmShuttingDown; + + /** + * Constructs this instance to listen to the VM shutdown call. + * + */ + public VmShutdownListener(final String name) + { + logger = LogFactory.getLog(VmShutdownListener.class); + + vmShuttingDown = false; + Runnable shutdownRunnable = new Runnable() + { + public void run() + { + vmShuttingDown = true; + if (logger.isDebugEnabled()) + { + logger.debug("VM shutdown detected by listener " + name); + } + }; + }; + Thread shutdownThread = new Thread(shutdownRunnable, "ShutdownListener-" + name); + Runtime.getRuntime().addShutdownHook(shutdownThread); + } + + /** + * @return Returns true if the VM shutdown signal was detected. + */ + public boolean isVmShuttingDown() + { + return vmShuttingDown; + } + + /** + * Message carrier to break out of loops using the callback. + * + * @author Derek Hulley + * @since 3.2.1 + */ + public static class VmShutdownException extends RuntimeException + { + private static final long serialVersionUID = -5876107469054587072L; + } +} diff --git a/src/main/java/org/alfresco/util/bean/BooleanBean.java b/src/main/java/org/alfresco/util/bean/BooleanBean.java new file mode 100644 index 0000000000..8d43227a1f --- /dev/null +++ b/src/main/java/org/alfresco/util/bean/BooleanBean.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.bean; + +import org.alfresco.api.AlfrescoPublicApi; + +/** + * Interface that may be implemented to return a boolean value in Spring bean configuration. + */ +@AlfrescoPublicApi +public interface BooleanBean +{ + public boolean isTrue(); +} diff --git a/src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java b/src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java new file mode 100644 index 0000000000..f086229763 --- /dev/null +++ b/src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.bean; + +import org.alfresco.util.PropertyCheck; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Factory bean to find beans using a class hierarchy to drive the lookup. The well-known + * placeholder {@link #DEFAULT_DIALECT_PLACEHOLDER} is replaced with successive class + * names starting from the {@link #setDialectClass(String) dialect class} and + * progressing up the hierarchy until the {@link #setDialectBaseClass(String) base class} + * is reached. The bean is looked up in the context at each point until the + * bean is found or the base of the class hierarchy is reached. + *

+ * For example assume bean names:
+ *

+ *    BEAN 1: contentDAO.org.hibernate.dialect.Dialect
+ *    BEAN 2: contentDAO.org.hibernate.dialect.MySQLInnoDBDialect
+ *    BEAN 3: propertyValueDAO.org.hibernate.dialect.Dialect
+ *    BEAN 4: propertyValueDAO.org.hibernate.dialect.MySQLDialect
+ * 
+ * and
+ *
+ *    dialectBaseClass = org.hibernate.dialect.Dialect
+ * 
+ * For dialect org.hibernate.dialect.MySQLInnoDBDialect the following will be returned:
+ *
+ *    contentDAO.bean.dialect == BEAN 2
+ *    propertyValueDAO.bean.dialect == BEAN 4
+ * 
+ * For dialectorg.hibernate.dialect.MySQLDBDialect the following will be returned:
+ *
+ *    contentDAO.bean.dialect == BEAN 1
+ *    propertyValueDAO.bean.dialect == BEAN 4
+ * 
+ * For dialectorg.hibernate.dialect.Dialect the following will be returned:
+ *
+ *    contentDAO.bean.dialect == BEAN 1
+ *    propertyValueDAO.bean.dialect == BEAN 3
+ * 
+ * + * @author Derek Hulley + * @since 3.2SP1 + */ +public class HierarchicalBeanLoader + implements InitializingBean, FactoryBean, ApplicationContextAware +{ + public static final String DEFAULT_DIALECT_PLACEHOLDER = "#bean.dialect#"; + public static final String DEFAULT_DIALECT_REGEX = "\\#bean\\.dialect\\#"; + + private ApplicationContext ctx; + private String targetBeanName; + private Class targetClass; + private String dialectBaseClass; + private String dialectClass; + + /** + * Create a new HierarchicalResourceLoader. + */ + public HierarchicalBeanLoader() + { + super(); + } + + /** + * The application context that this bean factory serves. + */ + public void setApplicationContext(ApplicationContext ctx) + { + this.ctx = ctx; + } + + /** + * @param targetBeanName the name of the target bean to return, + * including the {@link #DEFAULT_DIALECT_PLACEHOLDER} + * where the specific dialect must be replaced. + */ + public void setTargetBeanName(String targetBeanName) + { + this.targetBeanName = targetBeanName; + } + + /** + * Set the target class that will be returned by {@link #getObjectType()} + * + * @param targetClass the type that this factory returns + */ + public void setTargetClass(Class targetClass) + { + this.targetClass = targetClass; + } + + /** + * Set the class to be used during hierarchical dialect replacement. Searches for the + * configuration location will not go further up the hierarchy than this class. + * + * @param className the name of the class or interface + */ + public void setDialectBaseClass(String className) + { + this.dialectBaseClass = className; + } + + public void setDialectClass(String className) + { + this.dialectClass = className; + } + + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "targetBeanName", targetBeanName); + PropertyCheck.mandatory(this, "targetClass", targetClass); + PropertyCheck.mandatory(this, "dialectBaseClass", dialectBaseClass); + PropertyCheck.mandatory(this, "dialectClass", dialectClass); + } + + /** + * @return Returns {@link #setTargetClass(Class) target class} + */ + public Class getObjectType() + { + return targetClass; + } + + /** + * @return Returns true always + */ + public boolean isSingleton() + { + return true; + } + + /** + * Replaces the + */ + public Object getObject() throws Exception + { + if (dialectClass == null || dialectBaseClass == null) + { + ctx.getBean(targetBeanName); + } + + // If a property value has not been substituted, extract the property name and load from system + String dialectBaseClassStr = dialectBaseClass; + if (!PropertyCheck.isValidPropertyString(dialectBaseClass)) + { + String prop = PropertyCheck.getPropertyName(dialectBaseClass); + dialectBaseClassStr = System.getProperty(prop, dialectBaseClass); + } + String dialectClassStr = dialectClass; + if (!PropertyCheck.isValidPropertyString(dialectClass)) + { + String prop = PropertyCheck.getPropertyName(dialectClass); + dialectClassStr = System.getProperty(prop, dialectClass); + } + + Class dialectBaseClazz; + try + { + dialectBaseClazz = Class.forName(dialectBaseClassStr); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("Dialect base class not found: " + dialectBaseClassStr); + } + Class dialectClazz; + try + { + dialectClazz = Class.forName(dialectClassStr); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("Dialect class not found: " + dialectClassStr); + } + // Ensure that we are dealing with classes and not interfaces + if (!Object.class.isAssignableFrom(dialectBaseClazz)) + { + throw new RuntimeException( + "Dialect base class must be derived from java.lang.Object: " + + dialectBaseClazz.getName()); + } + if (!Object.class.isAssignableFrom(dialectClazz)) + { + throw new RuntimeException( + "Dialect class must be derived from java.lang.Object: " + + dialectClazz.getName()); + } + // We expect these to be in the same hierarchy + if (!dialectBaseClazz.isAssignableFrom(dialectClazz)) + { + throw new RuntimeException( + "Non-existent HierarchicalBeanLoader hierarchy: " + + dialectBaseClazz.getName() + " is not a superclass of " + dialectClazz); + } + + Class clazz = dialectClazz; + Object bean = null; + while (bean == null) + { + // Do replacement + String newBeanName = targetBeanName.replaceAll(DEFAULT_DIALECT_REGEX, clazz.getName()); + try + { + bean = ctx.getBean(newBeanName); + // Found it + break; + } + catch (NoSuchBeanDefinitionException e) + { + } + // Not found + bean = null; + // Are we at the base class? + if (clazz.equals(dialectBaseClazz)) + { + // We don't go any further + break; + } + // Move up the hierarchy + clazz = clazz.getSuperclass(); + if (clazz == null) + { + throw new RuntimeException( + "Non-existent HierarchicalBeanLoaderBean hierarchy: " + + dialectBaseClazz.getName() + " is not a superclass of " + dialectClazz); + } + } + return bean; + } +} diff --git a/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java b/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java new file mode 100644 index 0000000000..ce7012b112 --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java @@ -0,0 +1,732 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.cache; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.transaction.TransactionListener; +import org.alfresco.util.transaction.TransactionSupportUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.InitializingBean; + +/** + * The base implementation for an asynchronously refreshed cache. + * + * Currently supports one value or a cache per key (such as tenant.) Implementors just need to provide buildCache(String key/tennnantId) + * + * @author Andy + * @since 4.1.3 + * + * @author mrogers + * MER 17/04/2014 Refactored to core and generalised tennancy + */ +public abstract class AbstractAsynchronouslyRefreshedCache + implements AsynchronouslyRefreshedCache, + RefreshableCacheListener, + Callable, + BeanNameAware, + InitializingBean, + TransactionListener +{ + private static final String RESOURCE_KEY_TXN_DATA = "AbstractAsynchronouslyRefreshedCache.TxnData"; + + private static Log logger = LogFactory.getLog(AbstractAsynchronouslyRefreshedCache.class); + + private enum RefreshState + { + IDLE, WAITING, RUNNING, DONE + }; + + private ThreadPoolExecutor threadPoolExecutor; + private AsynchronouslyRefreshedCacheRegistry registry; + + // State + + private List listeners = new LinkedList(); + protected final ReentrantReadWriteLock liveLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock refreshLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock runLock = new ReentrantReadWriteLock(); + protected HashMap live = new HashMap(); + private LinkedHashSet refreshQueue = new LinkedHashSet(); + private String cacheId; + private RefreshState refreshState = RefreshState.IDLE; + private String resourceKeyTxnData; + + @Override + public void register(RefreshableCacheListener listener) + { + listeners.add(listener); + } + + /** + * @param threadPoolExecutor + * the threadPoolExecutor to set + */ + public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) + { + this.threadPoolExecutor = threadPoolExecutor; + } + + /** + * @param registry + * the registry to set + */ + public void setRegistry(AsynchronouslyRefreshedCacheRegistry registry) + { + this.registry = registry; + } + + + public void init() + { + registry.register(this); + } + + @Override + public String toString() + { + return "AbstractAsynchronouslyRefreshedCache [cacheId=" + cacheId + "]"; + } + + @Override + public T get(String key) + { + liveLock.readLock().lock(); + try + { + if (live.get(key) != null) + { + if (logger.isTraceEnabled()) + { + logger.trace("get() from cache for key " + key + " on " + this); + } + return live.get(key); + } + } + finally + { + liveLock.readLock().unlock(); + } + + if (logger.isDebugEnabled()) + { + logger.debug("get() miss, scheduling and waiting for key " + key + " on " + this); + } + + // There was nothing to return so we build and return + Refresh refresh = null; + refreshLock.writeLock().lock(); + try + { + // Is there anything we can wait for + for (Refresh existing : refreshQueue) + { + if (existing.getKey().equals(key)) + { + if (logger.isDebugEnabled()) + { + logger.debug("get() found existing build to wait for on " + this); + } + refresh = existing; + } + } + + if (refresh == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("get() building from scratch on " + this); + } + refresh = new Refresh(key); + refreshQueue.add(refresh); + } + + } + finally + { + refreshLock.writeLock().unlock(); + } + submit(); + waitForBuild(refresh); + + return get(key); + } + + /** + * Use the current thread to build and put a new version of the cache entry before returning. + * @param key the cache key + */ + public void forceInChangesForThisUncommittedTransaction(String key) + { + if (logger.isDebugEnabled()) + { + logger.debug("Building cache for tenant " + key + " on " + this); + } + T cache = buildCache(key); + if (logger.isDebugEnabled()) + { + logger.debug("Cache built for tenant " + key + " on " + this); + } + + liveLock.writeLock().lock(); + try + { + live.put(key, cache); + } + finally + { + liveLock.writeLock().unlock(); + } + } + + protected void waitForBuild(Refresh refresh) + { + while (refresh.getState() != RefreshState.DONE) + { + synchronized (refresh) + { + try + { + refresh.wait(100); + } + catch (InterruptedException e) + { + } + } + } + } + + @Override + public void refresh(String key) + { + // String tenantId = tenantService.getCurrentUserDomain(); + if (logger.isDebugEnabled()) + { + logger.debug("Async cache refresh request for tenant " + key + " on " + this); + } + registry.broadcastEvent(new RefreshableCacheRefreshEvent(cacheId, key), true); + } + + @Override + public void onRefreshableCacheEvent(RefreshableCacheEvent refreshableCacheEvent) + { + // Ignore events not targeted for this cache + if (!refreshableCacheEvent.getCacheId().equals(cacheId)) + { + return; + } + if (logger.isDebugEnabled()) + { + logger.debug("Async cache onRefreshableCacheEvent " + refreshableCacheEvent + " on " + this); + } + + // If in a transaction delay the refresh until after it commits + + if (TransactionSupportUtil.getTransactionId() != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("Async cache adding" + refreshableCacheEvent.getKey() + " to post commit list: " + this); + } + TransactionData txData = getTransactionData(); + txData.keys.add(refreshableCacheEvent.getKey()); + } + else + { + LinkedHashSet keys = new LinkedHashSet(); + keys.add(refreshableCacheEvent.getKey()); + queueRefreshAndSubmit(keys); + } + } + + /** + * To be used in a transaction only. + */ + private TransactionData getTransactionData() + { + TransactionData data = (TransactionData) TransactionSupportUtil.getResource(resourceKeyTxnData); + if (data == null) + { + data = new TransactionData(); + // create and initialize caches + data.keys = new LinkedHashSet(); + + // ensure that we get the transaction callbacks as we have bound the unique + // transactional caches to a common manager + TransactionSupportUtil.bindListener(this, 0); + TransactionSupportUtil.bindResource(resourceKeyTxnData, data); + } + return data; + } + + private void queueRefreshAndSubmit(LinkedHashSet tenantIds) + { + if((tenantIds == null) || (tenantIds.size() == 0)) + { + return; + } + refreshLock.writeLock().lock(); + try + { + for (String tenantId : tenantIds) + { + if (logger.isDebugEnabled()) + { + logger.debug("Async cache adding refresh to queue for tenant " + tenantId + " on " + this); + } + refreshQueue.add(new Refresh(tenantId)); + } + } + finally + { + refreshLock.writeLock().unlock(); + } + submit(); + } + + @Override + public boolean isUpToDate(String key) + { + refreshLock.readLock().lock(); + try + { + for(Refresh refresh : refreshQueue) + { + if(refresh.getKey().equals(key)) + { + return false; + } + } + if (TransactionSupportUtil.getTransactionId() != null) + { + return (!getTransactionData().keys.contains(key)); + } + else + { + return true; + } + } + finally + { + refreshLock.readLock().unlock(); + } + } + + /** + * Must be run with runLock.writeLock + */ + private Refresh getNextRefresh() + { + if (runLock.writeLock().isHeldByCurrentThread()) + { + for (Refresh refresh : refreshQueue) + { + if (refresh.state == RefreshState.WAITING) + { + return refresh; + } + } + return null; + } + else + { + throw new IllegalStateException("Method should not be called without holding the write lock: " + this); + } + + } + + /** + * Must be run with runLock.writeLock + */ + private int countWaiting() + { + int count = 0; + if (runLock.writeLock().isHeldByCurrentThread()) + { + refreshLock.readLock().lock(); + try + { + for (Refresh refresh : refreshQueue) + { + if (refresh.state == RefreshState.WAITING) + { + count++; + } + } + return count; + } + finally + { + refreshLock.readLock().unlock(); + } + } + else + { + throw new IllegalStateException("Method should not be called without holding the write lock: " + this); + } + + } + + private void submit() + { + runLock.writeLock().lock(); + try + { + if (refreshState == RefreshState.IDLE) + { + if (logger.isDebugEnabled()) + { + logger.debug("submit() scheduling job: " + this); + } + threadPoolExecutor.submit(this); + refreshState = RefreshState.WAITING; + } + } + finally + { + runLock.writeLock().unlock(); + } + } + + @Override + public Void call() + { + try + { + doCall(); + return null; + } + catch (Exception e) + { + logger.error("Cache update failed: " + this, e); + runLock.writeLock().lock(); + try + { + threadPoolExecutor.submit(this); + refreshState = RefreshState.WAITING; + } + finally + { + runLock.writeLock().unlock(); + } + return null; + } + } + + private void doCall() throws Exception + { + Refresh refresh = setUpRefresh(); + if (refresh == null) + { + return; + } + + if (logger.isDebugEnabled()) + { + logger.debug("Building cache for key" + refresh.getKey() + " on " + this); + } + + try + { + doRefresh(refresh); + } + catch (Exception e) + { + refresh.setState(RefreshState.WAITING); + throw e; + } + } + + private void doRefresh(Refresh refresh) + { + if (logger.isDebugEnabled()) + { + logger.debug("Building cache for tenant" + refresh.getKey() + ": " + this); + } + T cache = buildCache(refresh.getKey()); + if (logger.isDebugEnabled()) + { + logger.debug(".... cache built for tenant" + refresh.getKey()); + } + + liveLock.writeLock().lock(); + try + { + live.put(refresh.getKey(), cache); + } + finally + { + liveLock.writeLock().unlock(); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Cache entry updated for tenant" + refresh.getKey()); + } + + broadcastEvent(new RefreshableCacheRefreshedEvent(cacheId, refresh.key)); + + runLock.writeLock().lock(); + try + { + refreshLock.writeLock().lock(); + try + { + if (countWaiting() > 0) + { + if (logger.isDebugEnabled()) + { + logger.debug("Rescheduling more work: " + this); + } + threadPoolExecutor.submit(this); + refreshState = RefreshState.WAITING; + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Nothing to do; going idle: " + this); + } + refreshState = RefreshState.IDLE; + } + refresh.setState(RefreshState.DONE); + refreshQueue.remove(refresh); + } + finally + { + refreshLock.writeLock().unlock(); + } + } + finally + { + runLock.writeLock().unlock(); + } + } + + private Refresh setUpRefresh() throws Exception + { + Refresh refresh = null; + runLock.writeLock().lock(); + try + { + if (refreshState == RefreshState.WAITING) + { + refreshLock.writeLock().lock(); + try + { + refresh = getNextRefresh(); + if (refresh != null) + { + refreshState = RefreshState.RUNNING; + refresh.setState(RefreshState.RUNNING); + return refresh; + } + else + { + refreshState = RefreshState.IDLE; + return null; + } + } + finally + { + refreshLock.writeLock().unlock(); + } + } + else + { + return null; + } + } + catch (Exception e) + { + if (refresh != null) + { + refresh.setState(RefreshState.WAITING); + } + throw e; + } + finally + { + runLock.writeLock().unlock(); + } + + } + + @Override + public void setBeanName(String name) + { + cacheId = name; + + } + + @Override + public String getCacheId() + { + return cacheId; + } + + /** + * Build the cache entry for the specific key. + * This method is called in a thread-safe manner i.e. it is only ever called by a single + * thread. + * + * @param key + * @return new Cache instance + */ + protected abstract T buildCache(String key); + + private static class Refresh + { + private String key; + + private volatile RefreshState state = RefreshState.WAITING; + + Refresh(String key) + { + this.key = key; + } + + /** + * @return the tenantId + */ + public String getKey() + { + return key; + } + + /** + * @return the state + */ + public RefreshState getState() + { + return state; + } + + /** + * @param state + * the state to set + */ + public void setState(RefreshState state) + { + this.state = state; + } + + @Override + public int hashCode() + { + // The bucked is determined by the tenantId alone - we are going to change the state + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Refresh other = (Refresh) obj; + if (state != other.state) + return false; + if (key == null) + { + if (other.key != null) + return false; + } + else if (!key.equals(other.key)) + return false; + return true; + } + + @Override + public String toString() + { + return "Refresh [key=" + key + ", state=" + state + ", hashCode()=" + hashCode() + "]"; + } + + } + + @Override + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "threadPoolExecutor", threadPoolExecutor); + PropertyCheck.mandatory(this, "registry", registry); + registry.register(this); + + resourceKeyTxnData = RESOURCE_KEY_TXN_DATA + "." + cacheId; + + } + + public void broadcastEvent(RefreshableCacheEvent event) + { + if (logger.isDebugEnabled()) + { + logger.debug("Notifying cache listeners for " + getCacheId() + " " + event); + } + // If the system is up and running, broadcast the event immediately + for (RefreshableCacheListener listener : this.listeners) + { + listener.onRefreshableCacheEvent(event); + } + + } + + @Override + public void beforeCommit(boolean readOnly) + { + // Nothing + } + + @Override + public void beforeCompletion() + { + // Nothing + } + + @Override + public void afterCommit() + { + TransactionData txnData = getTransactionData(); + queueRefreshAndSubmit(txnData.keys); + } + + @Override + public void afterRollback() + { + // Nothing + } + + private static class TransactionData + { + LinkedHashSet keys; + } +} diff --git a/src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java b/src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java new file mode 100644 index 0000000000..d29a67a486 --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.cache; + +/** + * A generic event with the cache id and affected tenant + * + * @author Andy + */ +public abstract class AbstractRefreshableCacheEvent implements RefreshableCacheEvent +{ + private static final long serialVersionUID = 1324638640132648062L; + + private String cacheId; + private String key; + + AbstractRefreshableCacheEvent(String cacheId, String key) + { + this.cacheId = cacheId; + this.key = key; + } + + @Override + public String getCacheId() + { + return cacheId; + } + + @Override + public String getKey() + { + return key; + } + + @Override + public String toString() + { + return "AbstractRefreshableCacheEvent [cacheId=" + cacheId + ", tenantId=" + key + "]"; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((cacheId == null) ? 0 : cacheId.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + AbstractRefreshableCacheEvent other = (AbstractRefreshableCacheEvent) obj; + if (cacheId == null) + { + if (other.cacheId != null) return false; + } + else if (!cacheId.equals(other.cacheId)) return false; + if (key == null) + { + if (other.key != null) return false; + } + else if (!key.equals(other.key)) return false; + return true; + } +} diff --git a/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java b/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java new file mode 100644 index 0000000000..27534d1ae0 --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java @@ -0,0 +1,20 @@ +package org.alfresco.util.cache; + +public interface AsynchronouslyRefreshedCache extends RefreshableCache +{ + /** + * Get the cache id + * + * @return the cache ID + */ + String getCacheId(); + + /** + * Determine if the cache is up to date + * + * @param key tennant id + * @return true if the cache is not currently refreshing itself + */ + boolean isUpToDate(String key); + +} diff --git a/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java b/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java new file mode 100644 index 0000000000..8f093de68e --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java @@ -0,0 +1,19 @@ +package org.alfresco.util.cache; + + +public interface AsynchronouslyRefreshedCacheRegistry +{ + /** + * Register a listener + * @param listener + */ + public void register(RefreshableCacheListener listener); + + /** + * Fire an event + * @param event + * @param toAll - true goes to all listeners, false only to listeners that have a matching cacheId + */ + public void broadcastEvent(RefreshableCacheEvent event, boolean toAll); + +} diff --git a/src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java b/src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java new file mode 100644 index 0000000000..1e368bab87 --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java @@ -0,0 +1,74 @@ +package org.alfresco.util.cache; +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Base registry implementation + * + * @author Andy + */ +public class DefaultAsynchronouslyRefreshedCacheRegistry implements AsynchronouslyRefreshedCacheRegistry +{ + private static Log logger = LogFactory.getLog(DefaultAsynchronouslyRefreshedCacheRegistry.class); + + private List listeners = new LinkedList(); + + @Override + public void register(RefreshableCacheListener listener) + { + if(logger.isDebugEnabled()) + { + logger.debug("Listener added for " + listener.getCacheId()); + } + listeners.add(listener); + } + + public void broadcastEvent(RefreshableCacheEvent event, boolean toAll) + { + // If the system is up and running, broadcast the event immediately + for (RefreshableCacheListener listener : this.listeners) + { + if (toAll) + { + if(logger.isDebugEnabled()) + { + logger.debug("Delivering event (" + event + ") to listener (" + listener + ")."); + } + listener.onRefreshableCacheEvent(event); + } + else + { + if (listener.getCacheId().equals(event.getCacheId())) + { + if(logger.isDebugEnabled()) + { + logger.debug("Delivering event (" + event + ") to listener (" + listener + ")."); + } + listener.onRefreshableCacheEvent(event); + } + } + } + } +} + diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCache.java b/src/main/java/org/alfresco/util/cache/RefreshableCache.java new file mode 100644 index 0000000000..e9db992308 --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/RefreshableCache.java @@ -0,0 +1,29 @@ +package org.alfresco.util.cache; + + +public interface RefreshableCache +{ + /** + * Get the cache. + * If there is no cache value this call will block. + * If the underlying cache is being refreshed, the old cache value will be returned until the refresh is complete. + * + * @return T + */ + public T get(String key); + + /** + * Refresh the cache asynchronously. + */ + public void refresh(String key); + + /** + * Register to be informed when the cache is updated in the background. + * + * Note: it is up to the implementation to provide any transactional wrapping. + * Transactional wrapping is not required to invalidate a shared cache entry directly via a transactional cache + * @param listener RefreshableCacheListener + */ + void register(RefreshableCacheListener listener); + +} diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java b/src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java new file mode 100644 index 0000000000..2f225a4f57 --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java @@ -0,0 +1,18 @@ +package org.alfresco.util.cache; + +import java.io.Serializable; + +public interface RefreshableCacheEvent extends Serializable +{ + /** + * Get the cache id + */ + public String getCacheId(); + + + /** + * Get the affected key/tenant id + */ + public String getKey(); + +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java b/src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java new file mode 100644 index 0000000000..8a72faa8ef --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java @@ -0,0 +1,20 @@ +package org.alfresco.util.cache; + + +public interface RefreshableCacheListener +{ + /** + * Callback made when a cache refresh occurs + * + * @param refreshableCacheEvent the cache event + */ + public void onRefreshableCacheEvent(RefreshableCacheEvent refreshableCacheEvent); + + /** + * Cache id so broadcast can be constrained to matching caches + * + * @return the cache ID + */ + public String getCacheId(); + +} diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java b/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java new file mode 100644 index 0000000000..161cb3ea4e --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.cache; + +/** + * Describes an entry that is stale in the cache + * + * @author Andy + * + */ +public class RefreshableCacheRefreshEvent extends AbstractRefreshableCacheEvent +{ + /** + * @param cacheId + */ + RefreshableCacheRefreshEvent(String cacheId, String key) + { + super(cacheId, key); + } + + /** + * + */ + private static final long serialVersionUID = -8011932788039835334L; + +} diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java b/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java new file mode 100644 index 0000000000..ef9833a126 --- /dev/null +++ b/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.cache; + +/** + * Describes a new entry has been inserted in the cache. + * + * @author Andy + * + */ +public class RefreshableCacheRefreshedEvent extends AbstractRefreshableCacheEvent +{ + + /** + * + */ + private static final long serialVersionUID = 2352511592269578075L; + + /** + * @param cacheId + * @param key - the key/ tennant id + */ + RefreshableCacheRefreshedEvent(String cacheId, String key) + { + super(cacheId, key); + } + +} diff --git a/src/main/java/org/alfresco/util/collections/CollectionUtils.java b/src/main/java/org/alfresco/util/collections/CollectionUtils.java new file mode 100644 index 0000000000..fc82aa000a --- /dev/null +++ b/src/main/java/org/alfresco/util/collections/CollectionUtils.java @@ -0,0 +1,647 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util.collections; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeSet; + +import org.alfresco.util.Pair; + +/** + * @author Nick Smith + * @author Neil Mc Erlean + * @since 4.0 + */ +public abstract class CollectionUtils +{ + public static boolean isEmpty(Map map) + { + if (map == null) + { + return true; + } + return map.isEmpty(); + } + + public static boolean isEmpty(Collection items) + { + if(items == null) + { + return true; + } + return items.isEmpty(); + } + + /** + * This method merges two sets returning the union of both sets. + * + * @param first first set. can be null. + * @param second second set. can be null. + * @return the union of both sets. will not be null + */ + public static Set nullSafeMerge(Set first, Set second) + { + return nullSafeMerge(first, second, false); + } + + /** + * This method merges two sets returning the union of both sets. + * + * @param first first set. can be null. + * @param second second set. can be null. + * @param emptyResultIsNull if the result is empty, should we return null? + * @return the union of both sets or null. + */ + public static Set nullSafeMerge(Set first, Set second, boolean emptyResultIsNull) + { + Set result = new HashSet(); + + if (first != null) result.addAll(first); + if (second != null) result.addAll(second); + + if (result.isEmpty() && emptyResultIsNull) + { + result = null; + } + return result; + } + + /** + * This method merges two maps returning the union of both maps. + * + * @param first first map. can be null. + * @param second second map. can be null. + * @return the union of both maps. will not be null + */ + public static Map nullSafeMerge(Map first, Map second) + { + return nullSafeMerge(first, second, false); + } + + /** + * This method merges two maps returning the union of both maps. + * + * @param first first map. can be null. + * @param second second map. can be null. + * @param emptyResultIsNull if the result is empty, should we return null? + * @return the union of both maps, or null. + */ + public static Map nullSafeMerge(Map first, Map second, boolean emptyResultIsNull) + { + Map result = new HashMap(); + + if (first != null) result.putAll(first); + if (second != null) result.putAll(second); + + if (result.isEmpty() && emptyResultIsNull) + { + result = null; + } + return result; + } + + /** + * This method joins two lists returning the a single list consisting of the first followed by the second. + * + * @param first first list. can be null. + * @param second second list. can be null. + * @return the concatenation of both lists. will not be null + */ + public static List nullSafeAppend(List first, List second) + { + return nullSafeAppend(first, second, false); + } + + /** + * This method joins two lists returning the a single list consisting of the first followed by the second. + * + * @param first first list. can be null. + * @param second second list. can be null. + * @param emptyResultIsNull if the result is empty, should we return null? + * @return the concatenation of both lists or null + */ + public static List nullSafeAppend(List first, List second, boolean emptyResultIsNull) + { + List result = new ArrayList(); + + if (first != null) result.addAll(first); + if (second != null) result.addAll(second); + + if (result.isEmpty() && emptyResultIsNull) + { + result = null; + } + return result; + } + + public static final Function TO_STRING_TRANSFORMER = new Function() + { + public String apply(Object value) + { + return value.toString(); + } + }; + + /** + * Converts a {@link Collection} of values of type F to a {@link Serializable} {@link List} of values of type T. + * Filters out all values converted to null. + * @param From type + * @param To type + * @param values the values to convert. + * @param transformer Used to convert values. + * @return List + */ + public static List transform(Collection values, Function transformer) + { + if(values == null || values.isEmpty()) + { + return new ArrayList(); + } + List results = new ArrayList(values.size()); + for (F value : values) + { + T result = transformer.apply(value); + if(result != null) + { + results.add(result); + } + } + return results; + } + + /** + * Converts a {@link Map} having keys of type F to a new {@link Map} instance having keys of type T. The object references + * in the value set are copied to the transformed map, thus reusing the same objects. + * @param From type + * @param To type + * @param The value type of the before and after maps. + * @param map the map to convert. + * @param transformer Used to convert keys. + * @return a new Map instance with transformed keys and unchanged values. These values will be the same object references. + */ + public static Map transformKeys(Map map, Function transformer) + { + if(map == null || map.isEmpty()) + { + return new HashMap(); + } + Map results = new HashMap(map.size()); + for (Entry entry : map.entrySet()) + { + T transformedKey = transformer.apply(entry.getKey()); + results.put(transformedKey, entry.getValue()); + } + return results; + } + + /** + * Converts a {@link Collection} of values of type F to a {@link Serializable} {@link List} of values of type T. + * Filters out all values converted to null. + * @param From type + * @param To type + * @param values the values to convert. + * @param transformer Used to convert values. + * @return List + */ + public static List transform(Function transformer, F... values) + { + if(values == null || values.length<1) + { + return new ArrayList(); + } + List results = new ArrayList(values.length); + for (F value : values) + { + T result = transformer.apply(value); + if(result != null) + { + results.add(result); + } + } + return results; + } + + public static List toListOfStrings(Collection values) + { + return transform(values, TO_STRING_TRANSFORMER); + } + + /** + * This utility method converts a vararg of Objects into a Set. + * + * @param objects the objects to be added to the set + * @return a Set of objects (any equal objects will of course not be duplicated) + * @throws ClassCastException if any of the supplied objects are not of type T. + */ + public static Set asSet(T... objects) + { + Set result = new HashSet<>(); + for (T obj : objects) + { + result.add(obj); + } + + return result; + } + + /** + * This utility method converts a vararg of Objects into a Set. + * + * @param clazz the Set type to return. + * @param objects the objects to be added to the set + * @return a Set of objects (any equal objects will of course not be duplicated) + * @throws ClassCastException if any of the supplied objects are not of type T. + */ + public static Set asSet(Class clazz, Object... objects) + { + Set result = new HashSet(); + for (Object obj : objects) + { + @SuppressWarnings("unchecked") + T cast = (T) obj; + result.add(cast); + } + + return result; + } + + /** + * Returns a filtered {@link List} of values. Only values for which filter.apply(T) returns true are included in the {@link List} or returned values. + * @param The type of the {@link Collection} + * @param values the {@link Collection} to be filtered. + * @param filter the {@link Function} used to filter the {@link Collection}. + * @return the filtered {@link List} of values. + */ + public static List filter(Collection values, final Function filter) + { + return transform(values, new Function() + { + public T apply(T value) + { + if(filter.apply(value)) + { + return value; + } + return null; + } + }); + } + + /** + * This method flattens the provided collection of collections of values into a single + * {@code List} object containing each of the elements from the provided sub-collections. + *

+ * For example, {@code flatten( [1, 2], [3], [], [4, 5, 6] )} would produce a List like {@code [1, 2, 3, 4, 5, 6]}. + * Here, "[]" represents any Java collection. + * + * @param the element type of the collections. Note that this must be the same for all collections. + * @param values a collection of collections of elements to be flattened. + * @return a List containing the flattened elements. + */ + public static List flatten(Collection> values) + { + List results = new ArrayList(); + for (Collection collection : values) + { + if (collection != null) { results.addAll(collection); } + } + return results; + } + + /** + * See {@link #flatten(Collection)} + * @param collections a vararg of Collection objects to be flattened into a list. + * @return A flat List containing the elements of the provided collections. + * @since 5.0 + */ + @SafeVarargs + public static List flatten(Collection... collections) + { + List> listOfCollections = Arrays.asList(collections); + return CollectionUtils.flatten(listOfCollections); + } + + public static List transformFlat(Collection values, Function> transformer) + { + return flatten(transform(values, transformer)); + } + + /** + * Finds the first value for which acceptor returns true. + * @param T + * @param values Collection + * @param acceptor Function + * @return returns accepted value or null. + */ + public static T findFirst(Collection values, Function acceptor) + { + if (values != null ) + { + for (T value : values) + { + if (acceptor.apply(value)) + { + return value; + } + } + } + return null; + } + + /** + * Returns an immutable Serializable Set containing the values. + * @param T + * @param values T... + * @return Set + */ + public static Set unmodifiableSet(T... values) + { + return unmodifiableSet(Arrays.asList(values)); + } + + /** + * Returns an immutable Serializable Set containing the values. + * @param T + * @param values Collection + * @return Set + */ + public static Set unmodifiableSet(Collection values) + { + TreeSet set = new TreeSet(values); + return Collections.unmodifiableSet(set); + } + + /** + * @param values Collection + * @param transformer Function + * @return Map + */ + public static Map transformToMap(Collection values, + Function transformer) + { + if(isEmpty(values)) + { + return Collections.emptyMap(); + } + HashMap results = new HashMap(values.size()); + for (F value : values) + { + T result = transformer.apply(value); + results.put(value, result); + } + return results; + } + + /** + * This method can be used to filter a Map. Any keys in the supplied map, for which the supplied {@link Function filter function} + * returns true, will be included in the resultant Map, else they will not. + * + * @param map the map whose entries are to be filtered. + * @param filter the filter function which is applied to the key. + * @return a filtered map. + */ + public static Map filterKeys(Map map, Function filter) + { + Map results = new HashMap(); + Set> entries = map.entrySet(); + for (Entry entry : entries) + { + K key = entry.getKey(); + if(filter.apply(key)) + { + results.put(key, entry.getValue()); + } + } + return results; + } + + public static Map transform(Map map, + Function, Pair> transformer ) + { + Map results = new HashMap(map.size()); + for (Entry entry : map.entrySet()) + { + Pair pair = transformer.apply(entry); + if(pair!=null) + { + TK key = pair.getFirst(); + if (key != null) + { + results.put(key, pair.getSecond()); + } + } + } + return results; + } + + public static Filter containsFilter(final Collection values) + { + return new Filter() + { + public Boolean apply(T value) + { + return values.contains(value); + } + }; + } + + /** + * This method returns a new ArrayList which is the intersection of the two List parameters, based on {@link Object#equals(Object) equality} + * of their elements. + * The intersection list will contain elements in the order they have in list1 and any references in the resultant list will be + * to elements within list1 also. + * + * @return a new ArrayList whose values represent the intersection of the two Lists. + */ + public static List intersect(List list1, List list2) + { + if (list1 == null || list1.isEmpty() || list2 == null || list2.isEmpty()) + { + return Collections.emptyList(); + } + + List result = new ArrayList(); + result.addAll(list1); + + result.retainAll(list2); + + return result; + } + + /** + * This method returns a new HashMap which is the intersection of the two Map parameters, based on {@link Object#equals(Object) equality} + * of their entries. + * Any references in the resultant map will be to elements within map1. + * + * @return a new HashMap whose values represent the intersection of the two Maps. + */ + public static Map intersect(Map map1, Map map2) + { + if (map1 == null || map1.isEmpty() || map2 == null || map2.isEmpty()) + { + return Collections.emptyMap(); + } + + // We now know neither map is null. + Map result = new HashMap(); + for (Map.Entry item : map1.entrySet()) + { + V value = map2.get(item.getKey()); + if (value != null && value.equals(item.getValue())) + { + result.put(item.getKey(), item.getValue()); + } + } + + return result; + } + + /** + * This method returns a new HashSet which is the intersection of the two Set parameters, based on {@link Object#equals(Object) equality} + * of their elements. + * Any references in the resultant set will be to elements within set1. + * + * @return a new HashSet whose values represent the intersection of the two Sets. + */ + public static Set intersect(Set set1, Set set2) + { + if (set1 == null || set1.isEmpty() || set2 == null || set2.isEmpty()) + { + return Collections.emptySet(); + } + + Set result = new HashSet(); + result.addAll(set1); + + result.retainAll(set2); + + return result; + } + + /** + * Creates a new sorted map, based on the values from the given map and Comparator. + * + * @param map the map which needs to be sorted + * @param valueComparator the Comparator + * @return a new sorted map + */ + public static Map sortMapByValue(Map map, Comparator> valueComparator) + { + if (map == null) + { + return Collections.emptyMap(); + } + + List> entriesList = new LinkedList<>(map.entrySet()); + + // Sort based on the map's values + Collections.sort(entriesList, valueComparator); + + Map orderedMap = new LinkedHashMap<>(entriesList.size()); + for (Entry entry : entriesList) + { + orderedMap.put(entry.getKey(), entry.getValue()); + } + return orderedMap; + } + + /** + * This method offers convenient conversion from value-based comparators to entry-based comparators + * for use with {@link #sortMapByValue(Map, Comparator)} above. + *

+ * Call it like so: {@code CollectionUtils.toEntryComparator(valueComparator);} + * + * @param valueComparator a comparator which compares the value types from a Map. + * @return a comparator which takes Map.Entry objects from that Map and compares their values. + */ + public static Comparator> toEntryComparator(final Comparator valueComparator) + { + return new Comparator>() + { + @Override public int compare(Entry e1, Entry e2) + { + return valueComparator.compare(e1.getValue(), e2.getValue()); + } + }; + } + + /** + * This method returns a new List instance containing the same element objects as the provided + * list, but with the specified element having been moved left by the specified offset. + *

+ * If the offset would mean that the element would move beyond the start or end of the list, it will + * move only to the end. + * + * @param offset the number of places over which to move the specified element. + * @param element the element to be moved. + * @param list the list to be reordered. + * @return a new List instance containing the ordered elements. + * @throws NoSuchElementException if the list does not contain an element equal to the one specified. + */ + public static List moveLeft(int offset, T element, List list) + { + return moveRight(-offset, element, list); + } + + /** + * This method does the same as {@link #moveLeft(int, Object, List)} but it moves the specified element + * to the right instead of the left. + */ + public static List moveRight(int offset, T element, List list) + { + final int elementIndex = list.indexOf(element); + + if (elementIndex == -1) { throw new NoSuchElementException("Element not found in provided list."); } + + if (offset == 0) + { + return list; + } + else + { + int newElementIndex = elementIndex + offset; + + // Ensure that the element will not move off the end of the list. + if (newElementIndex >= list.size()) { newElementIndex = list.size() - 1; } + else if (newElementIndex < 0) { newElementIndex = 0; } + + List result = new ArrayList<>(list); + result.remove(element); + + result.add(newElementIndex, element); + + return result; + } + } +} diff --git a/src/main/java/org/alfresco/util/collections/EntryTransformer.java b/src/main/java/org/alfresco/util/collections/EntryTransformer.java new file mode 100644 index 0000000000..720db9b2b1 --- /dev/null +++ b/src/main/java/org/alfresco/util/collections/EntryTransformer.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util.collections; + +import java.util.Map.Entry; + +import org.alfresco.util.Pair; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public interface EntryTransformer extends Function, Pair> +{ + //NOOP +} diff --git a/src/main/java/org/alfresco/util/collections/Filter.java b/src/main/java/org/alfresco/util/collections/Filter.java new file mode 100644 index 0000000000..412811355f --- /dev/null +++ b/src/main/java/org/alfresco/util/collections/Filter.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util.collections; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public interface Filter extends Function +{ + //NOOP +} diff --git a/src/main/java/org/alfresco/util/collections/Function.java b/src/main/java/org/alfresco/util/collections/Function.java new file mode 100644 index 0000000000..0150caf352 --- /dev/null +++ b/src/main/java/org/alfresco/util/collections/Function.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util.collections; + +/** + * + * @author Nick Smith + * @since 4.0 + * + * @param From type + * @param To type + */ +public interface Function +{ + /** + * Converts a value of type F to a result of type T. + * @param value F + * @return T + */ + T apply(F value); +} diff --git a/src/main/java/org/alfresco/util/collections/JsonUtils.java b/src/main/java/org/alfresco/util/collections/JsonUtils.java new file mode 100644 index 0000000000..de719834ff --- /dev/null +++ b/src/main/java/org/alfresco/util/collections/JsonUtils.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util.collections; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.json.JSONArray; + +/** + * @author Nick Smith + * @since 4.0 + * + */ +public class JsonUtils +{ + + @SuppressWarnings("unchecked") + public static List transform(JSONArray values, Function transformer) + { + if(values == null || values.length()<1) + { + return Collections.emptyList(); + } + ArrayList results = new ArrayList(values.length()); + for (int i = 0; i < values.length(); i++) + { + T result = transformer.apply((F)values.opt(i)); + if(result != null) + { + results.add(result); + } + } + return results; + } + + public static List toListOfStrings(JSONArray values) + { + return transform(values, CollectionUtils.TO_STRING_TRANSFORMER); + } +} diff --git a/src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java b/src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java new file mode 100644 index 0000000000..38601c4cbb --- /dev/null +++ b/src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.exec; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.StringTokenizer; + +import org.alfresco.util.Pair; + +/** + * This class is used to tokenize strings used as parameters for {@link RuntimeExec} objects. + * Examples of such strings are as follows (ImageMagick-like parameters): + *

    + *
  • -font Helvetica -pointsize 50
  • + *
  • -font Helvetica -pointsize 50 -draw "circle 100,100 150,150"
  • + *
  • -font Helvetica -pointsize 50 -draw "gravity south fill black text 0,12 'CopyRight'"
  • + *
+ * The first is the simple case which would be parsed into Strings as follows: + * "-font", "Helvetica", "-pointsize", "50" + *

+ * The second is more complex in that it includes a quoted parameter, which would be parsed as a single String: + * "-font", "Helvetica", "-pointsize", "50", "circle 100,100 150,150" + * Note however that the quotation characters will be stripped from the token. + *

+ * The third shows an example with embedded quotation marks, which would parse to: + * "-font", "Helvetica", "-pointsize", "50", "gravity south fill black text 0,12 'CopyRight'" + * In this case, the embedded quotation marks (which must be different from those surrounding the parameter) + * are preserved in the extracted token. + *

+ * The class does not understand escaped quotes such as p1 p2 "a b c \"hello\" d" p4 + * + * @author Neil Mc Erlean + * @since 3.4.2 + */ +public class ExecParameterTokenizer +{ + /** + * The string to be tokenized. + */ + private final String str; + + /** + * The list of tokens, which will take account of quoted sections. + */ + private List tokens; + + public ExecParameterTokenizer(String str) + { + this.str = str; + } + + /** + * This method returns the tokens in a parameter string. + * Any tokens not contained within single or double quotes will be tokenized in the normal + * way i.e. by using whitespace separators and the standard StringTokenizer algorithm. + * Any tokens which are contained within single or double quotes will be returned as single + * String instances and will have their quote marks removed. + *

+ * See above for examples. + * + * @throws NullPointerException if the string to be tokenized was null. + */ + public List getAllTokens() + { + if (this.str == null) + { + throw new NullPointerException("Illegal null string cannot be tokenized."); + } + + if (tokens == null) + { + tokens = new ArrayList(); + + // Preserve original behaviour from RuntimeExec. + if (str.indexOf('\'') == -1 && str.indexOf('"') == -1) + { + // Contains no quotes. + for (StringTokenizer standardTokenizer = new StringTokenizer(str); standardTokenizer.hasMoreTokens(); ) + { + tokens.add(standardTokenizer.nextToken()); + } + } + else + { + // There are either single or double quotes or both. + // So we need to identify the quoted regions within the string. + List> quotedRegions = new ArrayList>(); + + for (Pair next = identifyNextQuotedRegion(str, 0); next != null; ) + { + quotedRegions.add(next); + next = identifyNextQuotedRegion(str, next.getSecond() + 1); + } + + // Now we've got a List of index pairs identifying the quoted regions. + // We need to get substrings of quoted and unquoted blocks, whilst maintaining order. + List substrings = getSubstrings(str, quotedRegions); + + for (Substring r : substrings) + { + tokens.addAll(r.getTokens()); + } + } + } + + return this.tokens; + } + + /** + * The substrings will be a list of quoted and unquoted substrings. + * The unquoted ones need to be further tokenized in the normal way. + * The quoted ones must not be tokenized, but need their quotes stripped off. + */ + private List getSubstrings(String str, List> quotedRegionIndices) + { + List result = new ArrayList(); + + int cursorPosition = 0; + for (Pair nextQuotedRegionIndices : quotedRegionIndices) + { + if (cursorPosition < nextQuotedRegionIndices.getFirst()) + { + int startIndexOfNextQuotedRegion = nextQuotedRegionIndices.getFirst() - 1; + result.add(new UnquotedSubstring(str.substring(cursorPosition, startIndexOfNextQuotedRegion))); + cursorPosition = startIndexOfNextQuotedRegion; + } + result.add(new QuotedSubstring(str.substring(nextQuotedRegionIndices.getFirst(), nextQuotedRegionIndices.getSecond()))); + cursorPosition = nextQuotedRegionIndices.getSecond(); + } + + // We've processed all the quoted regions, but there may be a final unquoted region + if (cursorPosition < str.length() - 1) + { + result.add(new UnquotedSubstring(str.substring(cursorPosition, str.length() - 1))); + } + + return result; + } + + private Pair identifyNextQuotedRegion(String str, int startingIndex) + { + int indexOfNextSingleQuote = str.indexOf('\'', startingIndex); + int indexOfNextDoubleQuote = str.indexOf('"', startingIndex); + + if (indexOfNextSingleQuote == -1 && indexOfNextDoubleQuote == -1) + { + // If there are no more quoted regions + return null; + } + else if (indexOfNextSingleQuote > -1 && indexOfNextDoubleQuote > -1) + { + // If there are both single and double quotes in the remainder of the string + // Then select the closest quote. + int indexOfNextQuote = Math.min(indexOfNextSingleQuote, indexOfNextDoubleQuote); + char quoteChar = str.charAt(indexOfNextQuote); + + return findIndexOfClosingQuote(str, indexOfNextQuote, quoteChar); + } + else + { + // Only one of the quote characters is present. + + int indexOfNextQuote = Math.max(indexOfNextSingleQuote, indexOfNextDoubleQuote); + char quoteChar = str.charAt(indexOfNextQuote); + + return findIndexOfClosingQuote(str, indexOfNextQuote, quoteChar); + } + } + + private Pair findIndexOfClosingQuote(String str, int indexOfStartingQuote, char quoteChar) + { + // So we know which type of quote char we're dealing with. Either ' or ". + // Now we need to find the closing quote. + int indexAfterClosingQuote = str.indexOf(quoteChar, indexOfStartingQuote + 1) + 1; // + 1 to search after opening quote. + 1 to give result including closing quote. + + if (indexAfterClosingQuote == 0) // -1 + 1 + { + // If no closing quote. + throw new IllegalArgumentException("No closing " + quoteChar + "quote in" + str); + } + + return new Pair(indexOfStartingQuote, indexAfterClosingQuote); + } + + /** + * Utility interface for a substring in a parameter string. + */ + public interface Substring + { + /** + * Gets all the tokens in a parameter string. + */ + public List getTokens(); + } + + /** + * A substring that is not surrounded by (single or double) quotes. + */ + public class UnquotedSubstring implements Substring + { + private final String regionString; + public UnquotedSubstring(String str) + { + this.regionString = str; + } + + public List getTokens() + { + StringTokenizer t = new StringTokenizer(regionString); + List result = new ArrayList(); + while (t.hasMoreTokens()) + { + result.add(t.nextToken()); + } + return result; + } + + public String toString() + { + return UnquotedSubstring.class.getSimpleName() + ": '" + regionString + '\''; + } + } + + /** + * A substring that is surrounded by (single or double) quotes. + */ + public class QuotedSubstring implements Substring + { + private final String regionString; + public QuotedSubstring(String str) + { + this.regionString = str; + } + + public List getTokens() + { + String stringWithoutQuotes = regionString.substring(1, regionString.length() -1); + return Arrays.asList(new String[] {stringWithoutQuotes}); + } + + public String toString() + { + return QuotedSubstring.class.getSimpleName() + ": '" + regionString + '\''; + } + } +} diff --git a/src/main/java/org/alfresco/util/exec/RuntimeExec.java b/src/main/java/org/alfresco/util/exec/RuntimeExec.java new file mode 100644 index 0000000000..3db9814d9b --- /dev/null +++ b/src/main/java/org/alfresco/util/exec/RuntimeExec.java @@ -0,0 +1,1005 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.exec; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.Timer; +import java.util.TimerTask; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This acts as a session similar to the java.lang.Process, but + * logs the system standard and error streams. + *

+ * The bean can be configured to execute a command directly, or be given a map + * of commands keyed by the os.name Java system property. In this map, + * the default key that is used when no match is found is the + * {@link #KEY_OS_DEFAULT *} key. + *

+ * Use the {@link #setProcessDirectory(String) processDirectory} property to change the default location + * from which the command executes. The process's environment can be configured using the + * {@link #setProcessProperties(Map) processProperties} property. + *

+ * Commands may use placeholders, e.g. + *


+ *    find
+ *    -name
+ *    ${filename}
+ * 
+ * The filename property will be substituted for any supplied value prior to + * each execution of the command. Currently, no checks are made to get or check the + * properties contained within the command string. It is up to the client code to + * dynamically extract the properties required if the required properties are not + * known up front. + *

+ * Sometimes, a variable may contain several arguments. . In this case, the arguments + * need to be tokenized using a standard StringTokenizer. To force tokenization + * of a value, use: + *


+ *    SPLIT:${userArgs}
+ * 
+ * You should not use this just to split up arguments that are known to require tokenization + * up front. The SPLIT: directive works for the entire argument and will not do anything + * if it is not at the beginning of the argument. Do not use SPLIT: to break up arguments + * that are fixed, so avoid doing this: + *

+ *    SPLIT:ls -lih
+ * 
+ * Instead, break the command up explicitly: + *

+ *    ls
+ *    -lih
+ * 
+ * + * Tokenization of quoted parameter values is handled by {@link ExecParameterTokenizer}, which + * describes the support in more detail. + * + * @author Derek Hulley + */ +public class RuntimeExec +{ + /** the key to use when specifying a command for any other OS: * */ + public static final String KEY_OS_DEFAULT = "*"; + + private static final String KEY_OS_NAME = "os.name"; + private static final int BUFFER_SIZE = 1024; + private static final String VAR_OPEN = "${"; + private static final String VAR_CLOSE = "}"; + private static final String DIRECTIVE_SPLIT = "SPLIT:"; + + private static Log logger = LogFactory.getLog(RuntimeExec.class); + private static Log transformerDebugLogger = LogFactory.getLog("org.alfresco.repo.content.transform.TransformerDebug"); + + private String[] command; + private Charset charset; + private boolean waitForCompletion; + private Map defaultProperties; + private String[] processProperties; + private File processDirectory; + private Set errCodes; + private Timer timer = new Timer(true); + + /** + * Default constructor. Initialize this instance by setting individual properties. + */ + public RuntimeExec() + { + this.charset = Charset.defaultCharset(); + this.waitForCompletion = true; + defaultProperties = Collections.emptyMap(); + processProperties = null; + processDirectory = null; + + // set default error codes + this.errCodes = new HashSet(2); + errCodes.add(1); + errCodes.add(2); + } + + public String toString() + { + + StringBuffer sb = new StringBuffer(256); + sb.append("RuntimeExec:\n") + .append(" command: "); + if (command == null) + { + // command is 'null', so there's nothing to toString + sb.append("'null'\n"); + } + else + { + for (String cmdStr : command) + { + sb.append(cmdStr).append(" "); + } + sb.append("\n"); + } + sb.append(" env props: ").append(Arrays.toString(processProperties)).append("\n") + .append(" dir: ").append(processDirectory).append("\n") + .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n"); + return sb.toString(); + } + + /** + * Set the command to execute regardless of operating system + * + * @param command an array of strings representing the command (first entry) and arguments + * + * @since 3.0 + */ + public void setCommand(String[] command) + { + this.command = command; + } + + /** + * Sets the assumed charset of OUT and ERR streams generated by the executed command. + * This defaults to the system default charset: {@link Charset#defaultCharset()}. + * + * @param charsetCode a supported character set code + * @throws UnsupportedCharsetException if the characterset code is not recognised by Java + */ + public void setCharset(String charsetCode) + { + this.charset = Charset.forName(charsetCode); + } + + /** + * Set whether to wait for completion of the command or not. If there is no wait for completion, + * then the return value of out and err buffers cannot be relied upon as the + * command may still be in progress. Failure is therefore not possible unless the calling thread + * waits for execution. + * + * @param waitForCompletion true (default) is to wait for the command to exit, + * or false to just return an exit code of 0 and whatever + * output is available at that point. + * + * @since 2.1 + */ + public void setWaitForCompletion(boolean waitForCompletion) + { + this.waitForCompletion = waitForCompletion; + } + + /** + * Supply a choice of commands to execute based on a mapping from the os.name system + * property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used + * to get a command where there is not direct match to the operating system key. + *

+ * Each command is an array of strings, the first of which represents the command and all subsequent + * entries in the array represent the arguments. All elements of the array will be checked for + * the presence of any substitution parameters (e.g. '{dir}'). The parameters can be set using the + * {@link #setDefaultProperties(Map) defaults} or by passing the substitution values into the + * {@link #execute(Map)} command. + *

+ * If parameters passed may be multiple arguments, or if the values provided in the map are themselves + * collections of arguments (not recommended), then prefix the value with SPLIT: to ensure that + * the value is tokenized before being passed to the command. Any values that are not split, will be + * passed to the command as single arguments. For example:
+ * 'SPLIT: dir . ..' becomes 'dir', '.' and '..'.
+ * 'SPLIT: dir ${path}' (if path is '. ..') becomes 'dir', '.' and '..'.
+ * The splitting occurs post-subtitution. Where the arguments are known, it is advisable to avoid + * SPLIT:. + * + * @param commandsByOS a map of command string arrays, keyed by operating system names + * + * @see #setDefaultProperties(Map) + * + * @since 3.0 + */ + public void setCommandsAndArguments(Map commandsByOS) + { + // get the current OS + String serverOs = System.getProperty(KEY_OS_NAME); + // attempt to find a match + String[] command = commandsByOS.get(serverOs); + if (command == null) + { + // go through the commands keys, looking for one that matches by regular expression matching + for (String osName : commandsByOS.keySet()) + { + // Ignore * options. It is dealt with later. + if (osName.equals(KEY_OS_DEFAULT)) + { + continue; + } + // Do regex match + if (serverOs.matches(osName)) + { + command = commandsByOS.get(osName); + break; + } + } + // if there is still no command, then check for the wildcard + if (command == null) + { + command = commandsByOS.get(KEY_OS_DEFAULT); + } + } + // check + if (command == null) + { + throw new AlfrescoRuntimeException( + "No command found for OS " + serverOs + " or '" + KEY_OS_DEFAULT + "': \n" + + " commands: " + commandsByOS); + } + this.command = command; + } + + /** + * Supply a choice of commands to execute based on a mapping from the os.name system + * property to the command to execute. The {@link #KEY_OS_DEFAULT *} key can be used + * to get a command where there is not direct match to the operating system key. + * + * @param commandsByOS a map of command string keyed by operating system names + * + * @deprecated Use {@link #setCommandsAndArguments(Map)} + */ + public void setCommandMap(Map commandsByOS) + { + // This is deprecated, so issue a warning + logger.warn( + "The bean RuntimeExec property 'commandMap' has been deprecated;" + + " use 'commandsAndArguments' instead. See https://issues.alfresco.com/jira/browse/ETHREEOH-579."); + Map fixed = new LinkedHashMap(7); + for (Map.Entry entry : commandsByOS.entrySet()) + { + String os = entry.getKey(); + String unparsedCmd = entry.getValue(); + StringTokenizer tokenizer = new StringTokenizer(unparsedCmd); + String[] cmd = new String[tokenizer.countTokens()]; + for (int i = 0; i < cmd.length; i++) + { + cmd[i] = tokenizer.nextToken(); + } + fixed.put(os, cmd); + } + setCommandsAndArguments(fixed); + } + + /** + * Set the default command-line properties to use when executing the command. + * These are properties that substitute variables defined in the command string itself. + * Properties supplied during execution will overwrite the default properties. + *

+ * null properties will be treated as an empty string for substitution + * purposes. + * + * @param defaultProperties property values + */ + public void setDefaultProperties(Map defaultProperties) + { + this.defaultProperties = defaultProperties; + } + + /** + * Set additional runtime properties (environment properties) that will used + * by the executing process. + *

+ * Any keys or properties that start and end with ${...} will be removed on the assumption + * that these are unset properties. null values are translated to empty strings. + * All keys and values are trimmed of leading and trailing whitespace. + * + * @param processProperties Runtime process properties + * + * @see Runtime#exec(String, String[], java.io.File) + */ + public void setProcessProperties(Map processProperties) + { + ArrayList processPropList = new ArrayList(processProperties.size()); + boolean hasPath = false; + String systemPath = System.getenv("PATH"); + for (Map.Entry entry : processProperties.entrySet()) + { + String key = entry.getKey(); + String value = entry.getValue(); + if (key == null) + { + continue; + } + if (value == null) + { + value = ""; + } + key = key.trim(); + value = value.trim(); + if (key.startsWith(VAR_OPEN) && key.endsWith(VAR_CLOSE)) + { + continue; + } + if (value.startsWith(VAR_OPEN) && value.endsWith(VAR_CLOSE)) + { + continue; + } + // If a path is specified, prepend it to the existing path + if (key.equals("PATH")) + { + if (systemPath != null && systemPath.length() > 0) + { + processPropList.add(key + "=" + value + File.pathSeparator + systemPath); + } + else + { + processPropList.add(key + "=" + value); + } + hasPath = true; + } + else + { + processPropList.add(key + "=" + value); + } + } + // If a path was not specified, inherit the current one + if (!hasPath && systemPath != null && systemPath.length() > 0) + { + processPropList.add("PATH=" + systemPath); + } + this.processProperties = processPropList.toArray(new String[processPropList.size()]); + } + + /** + * Adds a property to existed processProperties. + * Property should not be null or empty. + * If property with the same value already exists then no change is made. + * If property exists with a different value then old value is replaced with the new one. + * @param name - property name + * @param value - property value + */ + public void setProcessProperty(String name, String value) + { + boolean set = false; + + if (name == null || value == null) + return; + + name = name.trim(); + value = value.trim(); + + if (name.isEmpty() || value.isEmpty()) + return; + + String property = name + "=" + value; + + for (String prop : this.processProperties) + { + if (prop.equals(property)) + { + set = true; + break; + } + + if (prop.startsWith(name)) + { + String oldValue = prop.split("=")[1]; + prop.replace(oldValue, value); + set = true; + } + } + + if (!set) + { + String[] existedProperties = this.processProperties; + int epl = existedProperties.length; + String[] newProperties = Arrays.copyOf(existedProperties, epl + 1); + newProperties[epl] = property; + this.processProperties = newProperties; + set = true; + } + } + + + /** + * Set the runtime location from which the command is executed. + *

+ * If the value is an unsubsititued variable (${...}) then it is ignored. + * If the location is not visible at the time of setting, a warning is issued only. + * + * @param processDirectory the runtime location from which to execute the command + */ + public void setProcessDirectory(String processDirectory) + { + if (processDirectory.startsWith(VAR_OPEN) && processDirectory.endsWith(VAR_CLOSE)) + { + this.processDirectory = null; + } + else + { + this.processDirectory = new File(processDirectory); + if (!this.processDirectory.exists()) + { + logger.warn( + "The runtime process directory is not visible when setting property 'processDirectory': \n" + + this); + } + } + } + + /** + * A comma or space separated list of values that, if returned by the executed command, + * indicate an error value. This defaults to "1, 2". + * + * @param errCodesStr the error codes for the execution + */ + public void setErrorCodes(String errCodesStr) + { + errCodes.clear(); + StringTokenizer tokenizer = new StringTokenizer(errCodesStr, " ,"); + while(tokenizer.hasMoreElements()) + { + String errCodeStr = tokenizer.nextToken(); + // attempt to convert it to an integer + try + { + int errCode = Integer.parseInt(errCodeStr); + this.errCodes.add(errCode); + } + catch (NumberFormatException e) + { + throw new AlfrescoRuntimeException( + "Property 'errorCodes' must be comma-separated list of integers: " + errCodesStr); + } + } + } + + /** + * Executes the command using the default properties + * + * @see #execute(Map) + */ + public ExecutionResult execute() + { + return execute(defaultProperties); + } + + /** + * Executes the statement that this instance was constructed with. + * + * @param properties the properties that the command might be executed with. + * null properties will be treated as an empty string for substitution + * purposes. + * + * @return Returns the full execution results + */ + public ExecutionResult execute(Map properties) + { + return execute(properties, -1); + } + + /** + * Executes the statement that this instance was constructed with an optional + * timeout after which the command is asked to + * + * @param properties the properties that the command might be executed with. + * null properties will be treated as an empty string for substitution + * purposes. + * @param timeoutMs a timeout after which {@link Process#destroy()} is called. + * ignored if less than or equal to zero. Note this method does not guarantee + * to terminate the process (it is not a kill -9). + * + * @return Returns the full execution results + */ + public ExecutionResult execute(Map properties, final long timeoutMs) + { + int defaultFailureExitValue = errCodes.size() > 0 ? ((Integer)errCodes.toArray()[0]) : 1; + + // check that the command has been set + if (command == null) + { + throw new AlfrescoRuntimeException("Runtime command has not been set: \n" + this); + } + + // create the properties + Runtime runtime = Runtime.getRuntime(); + Process process = null; + String[] commandToExecute = null; + try + { + // execute the command with full property replacement + commandToExecute = getCommand(properties); + final Process thisProcess = runtime.exec(commandToExecute, processProperties, processDirectory); + process = thisProcess; + if (timeoutMs > 0) + { + final String[] command = commandToExecute; + timer.schedule(new TimerTask() + { + @Override + public void run() + { + // Only try to kill the process if it is still running + try + { + thisProcess.exitValue(); + } + catch (IllegalThreadStateException stillRunning) + { + if (transformerDebugLogger.isDebugEnabled()) + { + transformerDebugLogger.debug("Process has taken too long ("+ + (timeoutMs/1000)+" seconds). Killing process "+ + Arrays.deepToString(command)); + } + thisProcess.destroy(); + } + } + }, timeoutMs); + } + } + catch (IOException e) + { + // The process could not be executed here, so just drop out with an appropriate error state + String execOut = ""; + String execErr = e.getMessage(); + int exitValue = defaultFailureExitValue; + ExecutionResult result = new ExecutionResult(null, commandToExecute, errCodes, exitValue, execOut, execErr); + logFullEnvironmentDump(result); + return result; + } + + // create the stream gobblers + InputStreamReaderThread stdOutGobbler = new InputStreamReaderThread(process.getInputStream(), charset); + InputStreamReaderThread stdErrGobbler = new InputStreamReaderThread(process.getErrorStream(), charset); + + // start gobbling + stdOutGobbler.start(); + stdErrGobbler.start(); + + // wait for the process to finish + int exitValue = 0; + try + { + if (waitForCompletion) + { + exitValue = process.waitFor(); + } + } + catch (InterruptedException e) + { + // process was interrupted - generate an error message + stdErrGobbler.addToBuffer(e.toString()); + exitValue = defaultFailureExitValue; + } + + if (waitForCompletion) + { + // ensure that the stream gobblers get to finish + stdOutGobbler.waitForCompletion(); + stdErrGobbler.waitForCompletion(); + } + + // get the stream values + String execOut = stdOutGobbler.getBuffer(); + String execErr = stdErrGobbler.getBuffer(); + + // construct the return value + ExecutionResult result = new ExecutionResult(process, commandToExecute, errCodes, exitValue, execOut, execErr); + + // done + logFullEnvironmentDump(result); + return result; + } + + /** + * Dump the full environment in debug mode + */ + private void logFullEnvironmentDump(ExecutionResult result) + { + if (logger.isTraceEnabled()) + { + StringBuilder sb = new StringBuilder(); + sb.append(result); + + // Environment variables modified by Alfresco + if (processProperties != null && processProperties.length > 0) + { + sb.append("\n modified environment: "); + for (int i=0; i envVariables = System.getenv(); + for (Map.Entry entry : envVariables.entrySet()) + { + String name = entry.getKey(); + String value = entry.getValue(); + sb.append("\n "); + sb.append(name + "=" + value); + } + + logger.trace(sb); + } + else if (logger.isDebugEnabled()) + { + logger.debug(result); + } + + // close output stream (connected to input stream of native subprocess) + } + + /** + * @return Returns the command that will be executed if no additional properties + * were to be supplied + */ + public String[] getCommand() + { + return getCommand(defaultProperties); + } + + /** + * Get the command that will be executed post substitution. + *

+ * null properties will be treated as an empty string for substitution + * purposes. + * + * @param properties the properties that the command might be executed with + * @return Returns the command that will be executed should the additional properties + * be supplied + */ + public String[] getCommand(Map properties) + { + Map execProperties = null; + if (properties == defaultProperties) + { + // we are just using the default properties + execProperties = defaultProperties; + } + else + { + execProperties = new HashMap(defaultProperties); + // overlay the supplied properties + execProperties.putAll(properties); + } + // Perform the substitution for each element of the command + ArrayList adjustedCommandElements = new ArrayList(20); + for (int i = 0; i < command.length; i++) + { + StringBuilder sb = new StringBuilder(command[i]); + for (Map.Entry entry : execProperties.entrySet()) + { + String key = entry.getKey(); + String value = entry.getValue(); + // ignore null + if (value == null) + { + value = ""; + } + // progressively replace the property in the command + key = (VAR_OPEN + key + VAR_CLOSE); + int index = sb.indexOf(key); + while (index > -1) + { + // replace + sb.replace(index, index + key.length(), value); + // get the next one + index = sb.indexOf(key, index + 1); + } + } + String adjustedValue = sb.toString(); + // Now SPLIT: it + if (adjustedValue.startsWith(DIRECTIVE_SPLIT)) + { + String unsplitAdjustedValue = sb.substring(DIRECTIVE_SPLIT.length()); + + // There may be quoted arguments here (see ALF-7482) + ExecParameterTokenizer quoteAwareTokenizer = new ExecParameterTokenizer(unsplitAdjustedValue); + List tokens = quoteAwareTokenizer.getAllTokens(); + adjustedCommandElements.addAll(tokens); + } + else + { + adjustedCommandElements.add(adjustedValue); + } + } + // done + return adjustedCommandElements.toArray(new String[adjustedCommandElements.size()]); + } + + /** + * Object to carry the results of an execution to the caller. + * + * @author Derek Hulley + */ + public static class ExecutionResult + { + private final Process process; + private final String[] command; + private final Set errCodes; + private final int exitValue; + private final String stdOut; + private final String stdErr; + + /** + * + * @param process the process attached to Java - null is allowed + */ + private ExecutionResult( + final Process process, + final String[] command, + final Set errCodes, + final int exitValue, + final String stdOut, + final String stdErr) + { + this.process = process; + this.command = command; + this.errCodes = errCodes; + this.exitValue = exitValue; + this.stdOut = stdOut; + this.stdErr = stdErr; + } + + @Override + public String toString() + { + String out = stdOut.length() > 250 ? stdOut.substring(0, 250) : stdOut; + String err = stdErr.length() > 250 ? stdErr.substring(0, 250) : stdErr; + + StringBuilder sb = new StringBuilder(128); + sb.append("Execution result: \n") + .append(" os: ").append(System.getProperty(KEY_OS_NAME)).append("\n") + .append(" command: ");appendCommand(sb, command).append("\n") + .append(" succeeded: ").append(getSuccess()).append("\n") + .append(" exit code: ").append(exitValue).append("\n") + .append(" out: ").append(out).append("\n") + .append(" err: ").append(err); + return sb.toString(); + } + + /** + * Appends the command in a form that make running from the command line simpler. + * It is not a real attempt at making a command given all the operating system + * and shell options, but makes copy, paste and edit a bit simpler. + */ + private StringBuilder appendCommand(StringBuilder sb, String[] command) + { + boolean arg = false; + for (String element: command) + { + if (element == null) + { + continue; + } + + if (arg) + { + sb.append(' '); + } + else + { + arg = true; + } + + boolean escape = element.indexOf(' ') != -1 || element.indexOf('>') != -1; + if (escape) + { + sb.append("\""); + } + sb.append(element); + if (escape) + { + sb.append("\""); + } + } + return sb; + } + + /** + * A helper method to force a kill of the process that generated this result. This is + * useful in cases where the process started is not expected to exit, or doesn't exit + * quickly. If the {@linkplain RuntimeExec#setWaitForCompletion(boolean) "wait for completion"} + * flag is false then the process may still be running when this result is returned. + * + * @return + * true if the process was killed, otherwise false + */ + public boolean killProcess() + { + if (process == null) + { + return true; + } + try + { + process.destroy(); + return true; + } + catch (Throwable e) + { + logger.warn(e.getMessage()); + return false; + } + } + + /** + * @param exitValue the command exit value + * @return Returns true if the code is a listed failure code + * + * @see #setErrorCodes(String) + */ + private boolean isFailureCode(int exitValue) + { + return errCodes.contains((Integer)exitValue); + } + + /** + * @return Returns true if the command was deemed to be successful according to the + * failure codes returned by the execution. + */ + public boolean getSuccess() + { + return !isFailureCode(exitValue); + } + + public int getExitValue() + { + return exitValue; + } + + public String getStdOut() + { + return stdOut; + } + + public String getStdErr() + { + return stdErr; + } + } + + /** + * Gobbles an InputStream and writes it into a + * StringBuffer + *

+ * The reading of the input stream is buffered. + */ + public static class InputStreamReaderThread extends Thread + { + private final InputStream is; + private final Charset charset; + private final StringBuffer buffer; // we require the synchronization + private boolean completed; + + /** + * @param is an input stream to read - it will be wrapped in a buffer + * for reading + */ + public InputStreamReaderThread(InputStream is, Charset charset) + { + super(); + setDaemon(true); // must not hold up the VM if it is terminating + this.is = is; + this.charset = charset; + this.buffer = new StringBuffer(BUFFER_SIZE); + this.completed = false; + } + + public synchronized void run() + { + completed = false; + + byte[] bytes = new byte[BUFFER_SIZE]; + InputStream tempIs = null; + try + { + tempIs = new BufferedInputStream(is, BUFFER_SIZE); + int count = -2; + while (count != -1) + { + // do we have something previously read? + if (count > 0) + { + String toWrite = new String(bytes, 0, count, charset.name()); + buffer.append(toWrite); + } + // read the next set of bytes + count = tempIs.read(bytes); + } + // done + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to read stream", e); + } + finally + { + // close the input stream + if (tempIs != null) + { + try + { + tempIs.close(); + } + catch (Exception e) + { + } + } + // The thread has finished consuming the stream + completed = true; + // Notify waiters + this.notifyAll(); // Note: Method is synchronized + } + } + + /** + * Waits for the run to complete. + *

+ * Remember to start the thread first + */ + public synchronized void waitForCompletion() + { + while (!completed) + { + try + { + // release our lock and wait a bit + this.wait(1000L); // 200 ms + } + catch (InterruptedException e) + { + } + } + } + + /** + * @param msg the message to add to the buffer + */ + public void addToBuffer(String msg) + { + buffer.append(msg); + } + + public boolean isComplete() + { + return completed; + } + + /** + * @return Returns the current state of the buffer + */ + public String getBuffer() + { + return buffer.toString(); + } + } +} diff --git a/src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java b/src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java new file mode 100644 index 0000000000..b1293f16fd --- /dev/null +++ b/src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.exec; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; +import org.alfresco.util.bean.BooleanBean; +import org.alfresco.util.exec.RuntimeExec.ExecutionResult; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; + +/** + * Application bootstrap bean that is able to execute one or more + * native executable statements upon startup and shutdown. + * + * @author Derek Hulley + */ +public class RuntimeExecBootstrapBean extends AbstractLifecycleBean +{ + private static Log logger = LogFactory.getLog(RuntimeExecBootstrapBean.class); + + private List startupCommands; + private boolean failOnError; + private boolean killProcessesOnShutdown; + private boolean enabled; + + /** Keep track of the processes so that we can kill them on shutdown */ + private List executionResults; + + private Thread shutdownThread; + + /** + * Initializes the bean + *

    + *
  • failOnError = true
  • + *
  • killProcessesOnShutdown = true
  • + *
  • enabled = true
  • + *
+ */ + public RuntimeExecBootstrapBean() + { + this.startupCommands = Collections.emptyList(); + this.executionResults = new ArrayList(1); + failOnError = true; + killProcessesOnShutdown = true; + enabled = true; + } + + /** + * Set the commands to execute, in sequence, when the application context + * is initialized. + * + * @param startupCommands list of commands + */ + public void setStartupCommands(List startupCommands) + { + this.startupCommands = startupCommands; + } + + /** + * Set whether a process failure generates an error or not. Deviation from the default is + * useful if use as part of a process where the command or the codes generated by the + * execution may be ignored or avoided by the system. + * + * @param failOnError true (default) to issue an error message and throw an + * exception if the process fails to execute or generates an error + * return value. + * + * @since 2.1 + */ + public void setFailOnError(boolean failOnError) + { + this.failOnError = failOnError; + } + + /** + * Set whether or not to force a shutdown of successfully started processes. As most + * bootstrap processes are kicked off in order to provide the server with some or other + * service, this is true by default. + * + * @param killProcessesOnShutdown + * true to force any successfully executed commands' processes to + * be forcibly killed when the server shuts down. + * + * @since 2.1.0 + */ + public void setKillProcessesOnShutdown(boolean killProcessesOnShutdown) + { + this.killProcessesOnShutdown = killProcessesOnShutdown; + } + + /** + * Set whether or not the process should be disabled at ApplicationContext bootstrap. + * If a RuntimeExecBootstrapBean is disabled, then the command will not be executed. + * This property is not required and is false by default. + *

+ * This method has been deprecated in favour of a clearer name introduced in 3.3. + * See {@link #setEnabled}. + * + * @param disabledAtStartUp any String which equalsIgnoreCase("true") + * to prevent the command from being executed. + * @since 3.2.1 + * @deprecated Use {@link #setEnabled} instead, remembering that the boolean property should be inverted. + */ + public void setDisabledAtStartUp(String disabledAtStartUp) + { + boolean disabled = Boolean.parseBoolean(disabledAtStartUp); + this.setEnabled(Boolean.toString(!disabled)); + } + + /** + * Set whether or not the process should be enabled at ApplicationContext bootstrap. + * If a RuntimeExecBootstrapBean is not enabled, then the command will not be executed. + * This property is not required and is true by default. + * + * @param enabled any String which does not equalsIgnoreCase("true") + * will prevent the command from being executed. + * + * @since 3.3 + */ + public void setEnabled(String enabled) + { + // A String parameter rather than a boolean parameter is used here in order to allow + // the injection of properties ${foo.bar}. In this way undefined properties (which will + // be injected as "${foo.bar}") will mean the parameter is equivalent to false. + this.enabled = Boolean.parseBoolean(enabled); + } + + public void setEnabledFromBean(BooleanBean enabled) + { + this.enabled = enabled.isTrue(); + } + + @Override + protected synchronized void onBootstrap(ApplicationEvent event) + { + // If the command is disabled then do nothing. + if (this.enabled == false) + { + if (logger.isDebugEnabled()) + { + logger.debug("Bootstrap execution of " + startupCommands.size() + " was not enabled"); + } + return; + } + // execute + for (RuntimeExec command : startupCommands) + { + ExecutionResult result = command.execute(); + // check for failure + if (!result.getSuccess()) + { + String msg = "Bootstrap command failed: \n" + result; + if (failOnError) + { + throw new AlfrescoRuntimeException(msg); + } + else + { + logger.error(msg); + } + } + else + { + // It executed, so keep track of it + executionResults.add(result); + } + } + if (killProcessesOnShutdown) + { + // Force a shutdown on VM termination as we can't rely on the Spring context termination + this.shutdownThread = new KillProcessShutdownThread(); + Runtime.getRuntime().addShutdownHook(this.shutdownThread); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Bootstrap execution of " + startupCommands.size() + " commands was successful"); + } + } + + /** + * A thread that serves to kill the successfully created process, if required + * + * @since 2.1 + * @author Derek Hulley + */ + private class KillProcessShutdownThread extends Thread + { + public KillProcessShutdownThread() + { + super(RuntimeExecBootstrapBean.class.getName()); + } + @Override + public void run() + { + doShutdown(); + } + } + + /** + * Handle the shutdown of a subsystem but not the entire VM + */ + @Override + protected synchronized void onShutdown(ApplicationEvent event) + { + if (this.enabled == false) + { + return; + } + + try + { + // We managed to stop the process ourselves (e.g. on subsystem shutdown). Remove the shutdown hook + Runtime.getRuntime().removeShutdownHook(this.shutdownThread); + doShutdown(); + } + catch (IllegalStateException e) + { + // The system is shutting down - we'll have to let the shutdown hook run + } + } + + private void doShutdown() + { + if (!killProcessesOnShutdown) + { + // Do not force a kill + return; + } + for (ExecutionResult executionResult : executionResults) + { + executionResult.killProcess(); + } + } +} diff --git a/src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java b/src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java new file mode 100644 index 0000000000..842d217fa2 --- /dev/null +++ b/src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.exec; + +import java.util.Collections; +import java.util.List; + +import org.springframework.extensions.surf.util.AbstractLifecycleBean; +import org.alfresco.util.exec.RuntimeExec.ExecutionResult; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; + +/** + * This bean executes a list of shutdown commands when either the VM shuts down + * or the application context closes. In both cases, the commands are only + * executed if the application context was started. + * + * @author Derek Hulley + */ +public class RuntimeExecShutdownBean extends AbstractLifecycleBean +{ + private static Log logger = LogFactory.getLog(RuntimeExecShutdownBean.class); + + /** the commands to execute on context closure or VM shutdown */ + private List shutdownCommands; + /** the registered shutdown hook */ + private Thread shutdownHook; + /** ensures that commands don't get executed twice */ + private boolean executed; + + /** + * Initializes the bean with empty defaults, i.e. it will do nothing + */ + public RuntimeExecShutdownBean() + { + this.shutdownCommands = Collections.emptyList(); + this.executed = false; + } + + /** + * Set the commands to execute, in sequence, when the application context + * is initialized. + * + * @param startupCommands list of commands + */ + public void setShutdownCommands(List startupCommands) + { + this.shutdownCommands = startupCommands; + } + + private synchronized void execute() + { + // have we already done this? + if (executed) + { + return; + } + executed = true; + for (RuntimeExec command : shutdownCommands) + { + ExecutionResult result = command.execute(); + // check for failure + if (!result.getSuccess()) + { + logger.error("Shutdown command execution failed. Continuing with other commands.: \n" + result); + } + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Executed shutdown commands"); + } + } + + /** + * The thread that will call the shutdown commands. + * + * @author Derek Hulley + */ + private class ShutdownThread extends Thread + { + private ShutdownThread() + { + super(RuntimeExecShutdownBean.class.getName()); + this.setDaemon(true); + } + + @Override + public void run() + { + execute(); + } + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + // register shutdown hook + shutdownHook = new ShutdownThread(); + Runtime.getRuntime().addShutdownHook(shutdownHook); + + if (logger.isDebugEnabled()) + { + logger.debug("Registered shutdown hook"); + } + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // remove shutdown hook and execute + if (shutdownHook != null) + { + // execute + execute(); + // remove hook + try + { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } + catch (IllegalStateException e) + { + // VM is already shutting down + } + shutdownHook = null; + + if (logger.isDebugEnabled()) + { + logger.debug("Deregistered shutdown hook"); + } + } + } + +} + + + + + + + + + + + + + + diff --git a/src/main/java/org/alfresco/util/log/NDC.java b/src/main/java/org/alfresco/util/log/NDC.java new file mode 100644 index 0000000000..302f5a46ce --- /dev/null +++ b/src/main/java/org/alfresco/util/log/NDC.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.log; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * A stand in for the org.apache.log4j.NDC class that avoids introducing runtime dependencies against the otherwise + * optional log4j. + * + * @author dward + */ +public class NDC +{ + private static Log logger = LogFactory.getLog(NDC.class); + + /** Log4J delegate for NDC */ + private static NDCDelegate ndcDelegate; + + static + { + if (logger.isDebugEnabled()) + { + try + { + ndcDelegate = (NDCDelegate) Class.forName("org.alfresco.util.log.log4j.Log4JNDC").newInstance(); + } + catch (Throwable e) + { + // We just ignore it + } + } + } + + /** + * Push new diagnostic context information for the current thread. + * + * @param message + * The new diagnostic context information. + */ + public static void push(String message) + { + if (ndcDelegate != null) + { + ndcDelegate.push(message); + } + } + + /** + * Remove the diagnostic context for this thread. + */ + static public void remove() + { + if (ndcDelegate != null) + { + ndcDelegate.remove(); + } + } +} diff --git a/src/main/java/org/alfresco/util/log/NDCDelegate.java b/src/main/java/org/alfresco/util/log/NDCDelegate.java new file mode 100644 index 0000000000..1d9bbebcb2 --- /dev/null +++ b/src/main/java/org/alfresco/util/log/NDCDelegate.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.util.log; + +/** + * @author dward + * + */ +public interface NDCDelegate +{ + /** + * Push new diagnostic context information for the current thread. + * + * @param message + * The new diagnostic context information. + */ + public void push(String message); + + /** + * Remove the diagnostic context for this thread. + */ + public void remove(); +} diff --git a/src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java b/src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java new file mode 100644 index 0000000000..49f599f054 --- /dev/null +++ b/src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.util.log.log4j; + +import org.alfresco.util.log.NDCDelegate; +import org.apache.log4j.NDC; + +/** + * A stand in for the org.apache.log4j.NDC class that avoids introducing runtime dependencies against the otherwise + * optional log4j. + * + * @author dward + */ +public class Log4JNDC implements NDCDelegate +{ + // Force resolution of the log4j NDC class by the classloader (thus forcing an error if unavailable) + @SuppressWarnings("unused") + private static final Class NDC_REF = NDC.class; + + /** + * Push new diagnostic context information for the current thread. + * + * @param message + * The new diagnostic context information. + */ + public void push(String message) + { + NDC.push(message); + } + + /** + * Remove the diagnostic context for this thread. + */ + public void remove() + { + NDC.remove(); + } +} diff --git a/src/main/java/org/alfresco/util/random/NormalDistributionHelper.java b/src/main/java/org/alfresco/util/random/NormalDistributionHelper.java new file mode 100644 index 0000000000..a8c0f71d50 --- /dev/null +++ b/src/main/java/org/alfresco/util/random/NormalDistributionHelper.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.random; + +import org.apache.commons.math3.distribution.NormalDistribution; + +/** + * Utility functions guided by the + * Normal Distribution. + * + * @author Derek Hulley + * @since 5.1 + */ +public class NormalDistributionHelper +{ + private final NormalDistribution normalDistribution; + + /** + * Use a simple normal distribution to generate random numbers + */ + public NormalDistributionHelper() + { + this.normalDistribution = new NormalDistribution(); + } + + /** + * Get a random long where a standard deviation of 1.0 corresponds to the + * min and max values provided. The sampling is repeated until a value is + * found within the range given. + */ + public long getValue(long min, long max) + { + if (min > max) + { + throw new IllegalArgumentException("Min must less than or equal to max."); + } + + double sample = -2.0; + // Keep sampling until we get something within bounds of the standard deviation + while (sample < -1.0 || sample > 1.0) + { + sample = normalDistribution.sample(); + } + long halfRange = (max - min)/2L; + long mean = min + halfRange; + long ret = mean + (long) (halfRange * sample); + // Done + return ret; + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java b/src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java new file mode 100644 index 0000000000..19e92cc1d9 --- /dev/null +++ b/src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.registry; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.error.AlfrescoRuntimeException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * An generic registry of objects held by name. This is effectively a strongly-typed, + * synchronized map. + * + * @author Derek Hulley + * @since 3.2 + */ +@AlfrescoPublicApi +public class NamedObjectRegistry +{ + private static final Log logger = LogFactory.getLog(NamedObjectRegistry.class); + + private final ReentrantReadWriteLock.ReadLock readLock; + private final ReentrantReadWriteLock.WriteLock writeLock; + + private Class storageType; + private Pattern namePattern; + private final Map objects; + + /** + * Default constructor. The {@link #setStorageType(Class)} method must be called. + */ + public NamedObjectRegistry() + { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + readLock = lock.readLock(); + writeLock = lock.writeLock(); + this.namePattern = null; // Deliberately null + this.storageType = null; // Deliberately null + this.objects = new HashMap(13); + } + + /** + * Constructor that takes care of {@link #setStorageType(Class)}. + * + * @see #setStorageType(Class) + */ + public NamedObjectRegistry(Class type) + { + this(); + setStorageType(type); + } + + /** + * Set the type of class that the registry holds. Any attempt to register a + * an instance of another type will be rejected. + * + * @param clazz the type to store + */ + public void setStorageType(Class clazz) + { + writeLock.lock(); + try + { + this.storageType = clazz; + } + finally + { + writeLock.unlock(); + } + } + + /** + * Optionally set a pattern to which all object names must conform + * @param namePattern a regular expression + */ + public void setNamePattern(String namePattern) + { + writeLock.lock(); + try + { + this.namePattern = Pattern.compile(namePattern); + } + catch (PatternSyntaxException e) + { + throw new AlfrescoRuntimeException( + "Regular expression compilation failed for property 'namePrefix': " + e.getMessage(), + e); + } + finally + { + writeLock.unlock(); + } + } + + /** + * Register a named object instance. + * + * @param name the name of the object + * @param object the instance to register, which correspond to the type + */ + public void register(String name, T object) + { + ParameterCheck.mandatoryString("name", name); + ParameterCheck.mandatory("object", object); + + if (!storageType.isAssignableFrom(object.getClass())) + { + throw new IllegalArgumentException( + "This NameObjectRegistry only accepts objects of type " + storageType); + } + writeLock.lock(); + try + { + if (storageType == null) + { + throw new IllegalStateException( + "The registry has not been configured (setStorageType not yet called yet)"); + } + if (namePattern != null) + { + if (!namePattern.matcher(name).matches()) + { + throw new IllegalArgumentException( + "Object name '" + name + "' does not match required pattern: " + namePattern); + } + } + T prevObject = objects.put(name, object); + if (prevObject != null && prevObject != object) + { + logger.warn( + "Overwriting name object in registry: \n" + + " Previous: " + prevObject + "\n" + + " New: " + object); + } + } + finally + { + writeLock.unlock(); + } + } + + /** + * Get a named object if it has been registered + * + * @param name the name of the object to retrieve + * @return Returns the instance of the object, which will necessarily + * be of the correct type, or null + */ + public T getNamedObject(String name) + { + readLock.lock(); + try + { + // Get it + return objects.get(name); + } + finally + { + readLock.unlock(); + } + } + + /** + * @return Returns a copy of the map of instances + */ + public Map getAllNamedObjects() + { + readLock.lock(); + try + { + // Get it + return new HashMap(objects); + } + finally + { + readLock.unlock(); + } + } + + public void reset() + { + writeLock.lock(); + try + { + if (storageType == null) + objects.clear(); + } + finally + { + writeLock.unlock(); + } + } +} diff --git a/src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java b/src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java new file mode 100644 index 0000000000..87d845867f --- /dev/null +++ b/src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.resource; + +import org.alfresco.util.PropertyCheck; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; + +/** + * Locate resources by using a class hierarchy to drive the search. The well-known + * placeholder {@link #DEFAULT_DIALECT_PLACEHOLDER} is replaced with successive class + * names starting from the {@link #setDialectClass(String) dialect class} and + * progressing up the hierarchy until the {@link #setDialectBaseClass(String) base class} + * is reached. A full resource search using Spring's {@link DefaultResourceLoader} is + * done at each point until the resource is found or the base of the class hierarchy is + * reached. + *

+ * For example assume classpath resources:
+ *

+ *    RESOURCE 1: config/ibatis/org.hibernate.dialect.Dialect/SqlMap-DOG.xml
+ *    RESOURCE 2: config/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/SqlMap-DOG.xml
+ *    RESOURCE 3: config/ibatis/org.hibernate.dialect.Dialect/SqlMap-CAT.xml
+ *    RESOURCE 4: config/ibatis/org.hibernate.dialect.MySQLDialect/SqlMap-CAT.xml
+ * 
+ * and
+ *
+ *    dialectBaseClass = org.hibernate.dialect.Dialect
+ * 
+ * For dialect org.hibernate.dialect.MySQLInnoDBDialect the following will be returned:
+ *
+ *    config/ibatis/#resource.dialect#/SqlMap-DOG.xml == RESOURCE 2
+ *    config/ibatis/#resource.dialect#/SqlMap-CAT.xml == RESOURCE 4
+ * 
+ * For dialectorg.hibernate.dialect.MySQLDBDialect the following will be returned:
+ *
+ *    config/ibatis/#resource.dialect#/SqlMap-DOG.xml == RESOURCE 1
+ *    config/ibatis/#resource.dialect#/SqlMap-CAT.xml == RESOURCE 4
+ * 
+ * For dialectorg.hibernate.dialect.Dialect the following will be returned:
+ *
+ *    config/ibatis/#resource.dialect#/SqlMap-DOG.xml == RESOURCE 1
+ *    config/ibatis/#resource.dialect#/SqlMap-CAT.xml == RESOURCE 3
+ * 
+ * + * @author Derek Hulley + * @since 3.2 (Mobile) + */ +public class HierarchicalResourceLoader extends DefaultResourceLoader implements InitializingBean +{ + public static final String DEFAULT_DIALECT_PLACEHOLDER = "#resource.dialect#"; + public static final String DEFAULT_DIALECT_REGEX = "\\#resource\\.dialect\\#"; + + private String dialectBaseClass; + private String dialectClass; + + /** + * Create a new HierarchicalResourceLoader. + */ + public HierarchicalResourceLoader() + { + super(); + } + + /** + * Set the class to be used during hierarchical dialect replacement. Searches for the + * configuration location will not go further up the hierarchy than this class. + * + * @param className the name of the class or interface + */ + public void setDialectBaseClass(String className) + { + this.dialectBaseClass = className; + } + + public void setDialectClass(String className) + { + this.dialectClass = className; + } + + public void afterPropertiesSet() throws Exception + { + PropertyCheck.mandatory(this, "dialectBaseClass", dialectBaseClass); + PropertyCheck.mandatory(this, "dialectClass", dialectClass); + } + + /** + * Get a resource using the defined class hierarchy as a search path. + * + * @param location the location including a {@link #DEFAULT_DIALECT_PLACEHOLDER placeholder} + * @return a resource found by successive searches using class name replacement, or + * null if not found. + */ + @SuppressWarnings("unchecked") + @Override + public Resource getResource(String location) + { + if (dialectClass == null || dialectBaseClass == null) + { + return super.getResource(location); + } + + // If a property value has not been substituted, extract the property name and load from system + String dialectBaseClassStr = dialectBaseClass; + if (!PropertyCheck.isValidPropertyString(dialectBaseClass)) + { + String prop = PropertyCheck.getPropertyName(dialectBaseClass); + dialectBaseClassStr = System.getProperty(prop, dialectBaseClass); + } + String dialectClassStr = dialectClass; + if (!PropertyCheck.isValidPropertyString(dialectClass)) + { + String prop = PropertyCheck.getPropertyName(dialectClass); + dialectClassStr = System.getProperty(prop, dialectClass); + } + + Class dialectBaseClazz; + try + { + dialectBaseClazz = Class.forName(dialectBaseClassStr); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("Dialect base class not found: " + dialectBaseClassStr); + } + Class dialectClazz; + try + { + dialectClazz = Class.forName(dialectClassStr); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("Dialect class not found: " + dialectClassStr); + } + // Ensure that we are dealing with classes and not interfaces + if (!Object.class.isAssignableFrom(dialectBaseClazz)) + { + throw new RuntimeException( + "Dialect base class must be derived from java.lang.Object: " + + dialectBaseClazz.getName()); + } + if (!Object.class.isAssignableFrom(dialectClazz)) + { + throw new RuntimeException( + "Dialect class must be derived from java.lang.Object: " + + dialectClazz.getName()); + } + // We expect these to be in the same hierarchy + if (!dialectBaseClazz.isAssignableFrom(dialectClazz)) + { + throw new RuntimeException( + "Non-existent HierarchicalResourceLoader hierarchy: " + + dialectBaseClazz.getName() + " is not a superclass of " + dialectClazz); + } + + Class clazz = dialectClazz; + Resource resource = null; + while (resource == null) + { + // Do replacement + String newLocation = location.replaceAll(DEFAULT_DIALECT_REGEX, clazz.getName()); + resource = super.getResource(newLocation); + if (resource != null && resource.exists()) + { + // Found + break; + } + // Not found + resource = null; + // Are we at the base class? + if (clazz.equals(dialectBaseClazz)) + { + // We don't go any further + break; + } + // Move up the hierarchy + clazz = clazz.getSuperclass(); + if (clazz == null) + { + throw new RuntimeException( + "Non-existent HierarchicalResourceLoaderBean hierarchy: " + + dialectBaseClazz.getName() + " is not a superclass of " + dialectClazz); + } + } + return resource; + } +} diff --git a/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java b/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java new file mode 100644 index 0000000000..979d25471b --- /dev/null +++ b/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.shard; + +import java.util.LinkedList; +import java.util.List; + +/** + * Common ACL based index sharding behaviour for SOLR and the repository + * + * @author Andy + */ +public class ExplicitShardingPolicy +{ + private int numShards; + + private int replicationFactor; + + private int numNodes; + + public ExplicitShardingPolicy(int numShards, int replicationFactor, int numNodes) + { + this.numShards = numShards; + this.replicationFactor = replicationFactor; + this.numNodes = numNodes; + } + + public boolean configurationIsValid() + { + if ((numShards * replicationFactor) % numNodes != 0) + { + return false; + } + + int shardsPerNode = numShards * replicationFactor / numNodes; + if ((shardsPerNode > numShards) || (shardsPerNode < 1)) + { + return false; + } + + return true; + } + + public List getShardIdsForNode(int nodeInstance) + { + LinkedList shardIds = new LinkedList(); + int test = 0; + for (int replica = 0; replica < replicationFactor; replica++) + { + for (int shard = replica; shard < numShards + replica; shard++) + { + if (test % numNodes == nodeInstance - 1) + { + shardIds.add(shard % numShards); + } + test++; + } + + } + return shardIds; + } + + public List getNodeInstancesForShardId(int shardId) + { + LinkedList nodeInstances = new LinkedList(); + for (int nodeInstance = 1; nodeInstance <= numNodes; nodeInstance++) + { + int test = 0; + for (int replica = 0; replica < replicationFactor; replica++) + { + for (int shard = replica; shard < numShards + replica; shard++) + { + if (test % numNodes == nodeInstance - 1) + { + if(shard % numShards == shardId) + { + nodeInstances.add(nodeInstance); + } + } + test++; + } + + } + } + return nodeInstances; + } + +} diff --git a/src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java b/src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java new file mode 100644 index 0000000000..c2886846c8 --- /dev/null +++ b/src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.transaction; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Exception wraps {@link java.util.NoSuchElementException} from {@link org.apache.commons.dbcp.BasicDataSource} + * + * @author alex.mukha + * @since 4.1.9 + */ +public class ConnectionPoolException extends AlfrescoRuntimeException +{ + private static final long serialVersionUID = 1L; + + public ConnectionPoolException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } + + public ConnectionPoolException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + public ConnectionPoolException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + public ConnectionPoolException(String msgId) + { + super(msgId); + } +} diff --git a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java new file mode 100644 index 0000000000..08f41c6c7d --- /dev/null +++ b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java @@ -0,0 +1,618 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.transaction; + +import java.lang.reflect.Method; + +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.SystemException; +import javax.transaction.UserTransaction; + +import org.alfresco.error.StackTraceUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.transaction.CannotCreateTransactionException; +import org.springframework.transaction.NoTransactionException; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.interceptor.TransactionAspectSupport; +import org.springframework.transaction.interceptor.TransactionAttribute; +import org.springframework.transaction.interceptor.TransactionAttributeSource; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * A UserTransaction that will allow the thread using it to participate + * in transactions that are normally only begun and committed by the SpringFramework + * transaction aware components. + *

+ * Client code can use this class directly, but should be very careful to handle the exception + * conditions with the appropriate finally blocks and rollback code. + * It is recommended that clients use this class indirectly via an instance of the + * {@link org.alfresco.repo.transaction.RetryingTransactionHelper}. + *

+ * Nested user transaction are allowed. + *

+ * Logging:
+ * To dump exceptions during commits, turn debugging on for this class.
+ * To log leaked transactions i.e. a begin() is not matched by a commit() or rollback(), + * add .trace to the usual classname-based debug category and set to WARN log + * level. This will log the first detection of a leaked transaction and automatically enable + * transaction call stack logging for subsequent leaked transactions. To enforce + * call stack logging from the start set the .trace log level to DEBUG. Call stack + * logging will hamper performance but is useful when it appears that something is eating + * connections or holding onto resources - usually a sign that client code hasn't handled all + * possible exception conditions. + * + * @see org.springframework.transaction.PlatformTransactionManager + * @see org.springframework.transaction.support.DefaultTransactionDefinition + * + * @author Derek Hulley + */ +public class SpringAwareUserTransaction + extends TransactionAspectSupport + implements UserTransaction, TransactionAttributeSource, TransactionAttribute +{ + /* + * There is some extra work in here to perform safety checks against the thread ID. + * This is because this class doesn't operate in an environment that guarantees that the + * thread coming into the begin() method is the same as the thread forcing commit() or + * rollback(). + */ + + private static final long serialVersionUID = 3762538897183224373L; + + + private static final String NAME = "UserTransaction"; + + private static final Log logger = LogFactory.getLog(SpringAwareUserTransaction.class); + + + /* + * Leaked Transaction Logging + */ + private static final Log traceLogger = LogFactory.getLog(SpringAwareUserTransaction.class.getName() + ".trace"); + private static volatile boolean isCallStackTraced = false; + + static + { + if (traceLogger.isDebugEnabled()) + { + isCallStackTraced = true; + traceLogger.warn("Logging of transaction call stack is enforced and will affect performance"); + } + } + + + static boolean isCallStackTraced() + { + return isCallStackTraced; + } + + /** stores whether begin() & commit()/rollback() methods calls are balanced */ + private boolean isBeginMatched = true; + /** stores the begin() call stack when auto tracing */ + private StackTraceElement[] beginCallStack; + + + private boolean readOnly; + private int isolationLevel; + private int propagationBehaviour; + private int timeout; + + /** Stores the user transaction current status as affected by explicit operations */ + private int internalStatus = Status.STATUS_NO_TRANSACTION; + /** the transaction information used to check for mismatched begin/end */ + private TransactionInfo internalTxnInfo; + /** keep the thread that the transaction was started on to perform thread safety checks */ + private long threadId = Long.MIN_VALUE; + /** make sure that we clean up the thread transaction stack properly */ + private boolean finalized = false; + + /** + * Creates a user transaction that defaults to {@link TransactionDefinition#PROPAGATION_REQUIRED}. + * + * @param transactionManager the transaction manager to use + * @param readOnly true to force a read-only transaction + * @param isolationLevel one of the + * {@link TransactionDefinition#ISOLATION_DEFAULT TransactionDefinition.ISOLATION_XXX} + * constants + * @param propagationBehaviour one of the + * {@link TransactionDefinition#PROPAGATION_MANDATORY TransactionDefinition.PROPAGATION__XXX} + * constants + * @param timeout the transaction timeout in seconds. + * + * @see TransactionDefinition#getTimeout() + */ + public SpringAwareUserTransaction( + PlatformTransactionManager transactionManager, + boolean readOnly, + int isolationLevel, + int propagationBehaviour, + int timeout) + { + super(); + setTransactionManager(transactionManager); + setTransactionAttributeSource(this); + this.readOnly = readOnly; + this.isolationLevel = isolationLevel; + this.propagationBehaviour = propagationBehaviour; + this.timeout = timeout; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(256); + sb.append("UserTransaction") + .append("[object=").append(super.toString()) + .append(", status=").append(internalStatus) + .append("]"); + return sb.toString(); + } + + /** + * This class carries all the information required to fullfil requests about the transaction + * attributes. It acts as a source of the transaction attributes. + * + * @return Return this instance + */ + public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) + { + return this; + } + + /** + * Return a qualifier value associated with this transaction attribute. This is not used as the transaction manager + * has been selected for us. + * + * @return null always + */ + public String getQualifier() + { + return null; + } + + /** + * The {@link UserTransaction } must rollback regardless of the error. The + * {@link #rollback() rollback} behaviour is implemented by simulating a caught + * exception. As this method will always return true, the rollback + * behaviour will be to rollback the transaction or mark it for rollback. + * + * @return Returns true always + */ + public boolean rollbackOn(Throwable ex) + { + return true; + } + + /** + * @see #NAME + */ + public String getName() + { + return NAME; + } + + public boolean isReadOnly() + { + return readOnly; + } + + public int getIsolationLevel() + { + return isolationLevel; + } + + public int getPropagationBehavior() + { + return propagationBehaviour; + } + + public int getTimeout() + { + return timeout; + } + + /** + * Implementation required for {@link UserTransaction}. + */ + public void setTransactionTimeout(int timeout) throws SystemException + { + if (internalStatus != Status.STATUS_NO_TRANSACTION) + { + throw new RuntimeException("Can only set the timeout before begin"); + } + this.timeout = timeout; + } + + /** + * Gets the current transaction info, or null if none exists. + *

+ * A check is done to ensure that the transaction info on the stack is exactly + * the same instance used when this transaction was started. + * The internal status is also checked against the transaction info. + * These checks ensure that the transaction demarcation is done correctly and that + * thread safety is adhered to. + * + * @return Returns the current transaction + */ + private TransactionInfo getTransactionInfo() + { + // a few quick self-checks + if (threadId < 0 && internalStatus != Status.STATUS_NO_TRANSACTION) + { + throw new RuntimeException("Transaction has been started but there is no thread ID"); + } + else if (threadId >= 0 && internalStatus == Status.STATUS_NO_TRANSACTION) + { + throw new RuntimeException("Transaction has not been started but a thread ID has been recorded"); + } + + TransactionInfo txnInfo = null; + try + { + txnInfo = TransactionAspectSupport.currentTransactionInfo(); + // we are in a transaction + } + catch (NoTransactionException e) + { + // No transaction. It is possible that the transaction threw an exception during commit. + } + // perform checks for active transactions + if (internalStatus == Status.STATUS_ACTIVE) + { + if (Thread.currentThread().getId() != threadId) + { + // the internally stored transaction info (retrieved in begin()) should match the info + // on the thread + throw new RuntimeException("UserTransaction may not be accessed by multiple threads"); + } + else if (txnInfo == null) + { + // internally we recorded a transaction starting, but there is nothing on the thread + throw new RuntimeException("Transaction boundaries have been made to overlap in the stack"); + } + else if (txnInfo != internalTxnInfo) + { + // the transaction info on the stack isn't the one we started with + throw new RuntimeException("UserTransaction begin/commit mismatch"); + } + } + return txnInfo; + } + + /** + * This status is a combination of the internal status, as recorded during explicit operations, + * and the status provided by the Spring support. + * + * @see Status + */ + public synchronized int getStatus() throws SystemException + { + TransactionInfo txnInfo = getTransactionInfo(); + + // if the txn info is null, then we are outside a transaction + if (txnInfo == null) + { + return internalStatus; // this is checked in getTransactionInfo + } + + // normally the internal status is correct, but we only need to double check + // for the case where the transaction was marked for rollback, or rolledback + // in a deeper transaction + TransactionStatus txnStatus = txnInfo.getTransactionStatus(); + if (internalStatus == Status.STATUS_ROLLEDBACK) + { + // explicitly rolled back at some point + return internalStatus; + } + else if (txnStatus.isRollbackOnly()) + { + // marked for rollback at some point in the stack + return Status.STATUS_MARKED_ROLLBACK; + } + else + { + // just rely on the internal status + return internalStatus; + } + } + + public synchronized void setRollbackOnly() throws IllegalStateException, SystemException + { + // just a check + TransactionInfo txnInfo = getTransactionInfo(); + + int status = getStatus(); + // check the status + if (status == Status.STATUS_MARKED_ROLLBACK) + { + // this is acceptable + } + else if (status == Status.STATUS_NO_TRANSACTION) + { + throw new IllegalStateException("The transaction has not been started yet"); + } + else if (status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) + { + throw new IllegalStateException("The transaction has already been rolled back"); + } + else if (status == Status.STATUS_COMMITTING || status == Status.STATUS_COMMITTED) + { + throw new IllegalStateException("The transaction has already been committed"); + } + else if (status != Status.STATUS_ACTIVE) + { + throw new IllegalStateException("The transaction is not active: " + status); + } + + // mark for rollback + txnInfo.getTransactionStatus().setRollbackOnly(); + // make sure that we record the fact that we have been marked for rollback + internalStatus = Status.STATUS_MARKED_ROLLBACK; + // done + if (logger.isDebugEnabled()) + { + logger.debug("Set transaction status to rollback only: " + this); + } + } + + /** + * @throws NotSupportedException if an attempt is made to reuse this instance + */ + public synchronized void begin() throws NotSupportedException, SystemException + { + // make sure that the status and info align - the result may or may not be null + @SuppressWarnings("unused") + TransactionInfo txnInfo = getTransactionInfo(); + if (internalStatus != Status.STATUS_NO_TRANSACTION) + { + throw new NotSupportedException("The UserTransaction may not be reused"); + } + + // check + + if( (propagationBehaviour != TransactionDefinition.PROPAGATION_REQUIRES_NEW)) + { + if(!readOnly && + TransactionSynchronizationManager.isSynchronizationActive() && + TransactionSynchronizationManager.isCurrentTransactionReadOnly() + ) + { + throw new IllegalStateException("Nested writable transaction in a read only transaction"); + } + } + + // begin a transaction + try + { + internalTxnInfo = createTransactionIfNecessary( + (Method) null, + (Class) null); // super class will just pass nulls back to us + } + catch (CannotCreateTransactionException e) + { + throw new ConnectionPoolException("The DB connection pool is depleted.", e); + } + + internalStatus = Status.STATUS_ACTIVE; + threadId = Thread.currentThread().getId(); + + // Record that transaction details now that begin was successful + isBeginMatched = false; + if (isCallStackTraced) + { + // get the stack trace + Exception e = new Exception(); + e.fillInStackTrace(); + beginCallStack = e.getStackTrace(); + } + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Began user transaction: " + this); + } + } + + /** + * @throws IllegalStateException if a transaction was not started + */ + public synchronized void commit() + throws RollbackException, HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException + { + // perform checks + TransactionInfo txnInfo = getTransactionInfo(); + + int status = getStatus(); + // check the status + if (status == Status.STATUS_NO_TRANSACTION) + { + throw new IllegalStateException("The transaction has not yet begun"); + } + else if (status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) + { + throw new RollbackException("The transaction has already been rolled back"); + } + else if (status == Status.STATUS_MARKED_ROLLBACK) + { + throw new RollbackException("The transaction has already been marked for rollback"); + } + else if (status == Status.STATUS_COMMITTING || status == Status.STATUS_COMMITTED) + { + throw new IllegalStateException("The transaction has already been committed"); + } + else if (status != Status.STATUS_ACTIVE || txnInfo == null) + { + throw new IllegalStateException("No user transaction is active"); + } + + if (!finalized) + { + try + { + // the status seems correct - we can try a commit + commitTransactionAfterReturning(txnInfo); + } + catch (Throwable e) + { + if (logger.isDebugEnabled()) + { + logger.debug("Transaction didn't commit", e); + } + // commit failed + internalStatus = Status.STATUS_ROLLEDBACK; + RollbackException re = new RollbackException("Transaction didn't commit: " + e.getMessage()); + // Stick the originating reason for failure into the exception. + re.initCause(e); + throw re; + } + finally + { + // make sure that we clean up the stack + cleanupTransactionInfo(txnInfo); + finalized = true; + // clean up leaked transaction logging + isBeginMatched = true; + beginCallStack = null; + } + } + + // regardless of whether the transaction was finally committed or not, the status + // as far as UserTransaction is concerned should be 'committed' + + // keep track that this UserTransaction was explicitly committed + internalStatus = Status.STATUS_COMMITTED; + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Committed user transaction: " + this); + } + } + + public synchronized void rollback() + throws IllegalStateException, SecurityException, SystemException + { + // perform checks + TransactionInfo txnInfo = getTransactionInfo(); + + int status = getStatus(); + // check the status + if (status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) + { + throw new IllegalStateException("The transaction has already been rolled back"); + } + else if (status == Status.STATUS_COMMITTING || status == Status.STATUS_COMMITTED) + { + throw new IllegalStateException("The transaction has already been committed"); + } + else if (txnInfo == null) + { + throw new IllegalStateException("No user transaction is active"); + } + + if (!finalized) + { + try + { + // force a rollback by generating an exception that will trigger a rollback + completeTransactionAfterThrowing(txnInfo, new Exception()); + } + finally + { + // make sure that we clean up the stack + cleanupTransactionInfo(txnInfo); + finalized = true; + // clean up leaked transaction logging + isBeginMatched = true; + beginCallStack = null; + } + } + + // the internal status notes that we were specifically rolled back + internalStatus = Status.STATUS_ROLLEDBACK; + + // done + if (logger.isDebugEnabled()) + { + logger.debug("Rolled back user transaction: " + this); + } + } + + @Override + protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) + { + if (logger.isDebugEnabled()) + { + logger.debug("Exception attempting to pass transaction boundaries.", ex); + } + super.completeTransactionAfterThrowing(txInfo, ex); + } + + @Override + protected String methodIdentification(Method method) + { + // note: override for debugging purposes - this method called by Spring + return NAME; + } + + @Override + protected void finalize() throws Throwable + { + if (!isBeginMatched) + { + if (isCallStackTraced) + { + if (beginCallStack == null) + { + traceLogger.error("UserTransaction being garbage collected without a commit() or rollback(). " + + "NOTE: Prior to transaction call stack logging."); + } + else + { + StringBuilder sb = new StringBuilder(1024); + StackTraceUtil.buildStackTrace( + "UserTransaction being garbage collected without a commit() or rollback().", + beginCallStack, + sb, + -1); + traceLogger.error(sb); + } + } + else + { + traceLogger.error("Detected first UserTransaction which is being garbage collected without a commit() or rollback()"); + traceLogger.error("Logging of transaction call stack is now enabled and will affect performance"); + isCallStackTraced = true; + } + } + } +} diff --git a/src/main/java/org/alfresco/util/transaction/TransactionListener.java b/src/main/java/org/alfresco/util/transaction/TransactionListener.java new file mode 100644 index 0000000000..e97222ad5a --- /dev/null +++ b/src/main/java/org/alfresco/util/transaction/TransactionListener.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.transaction; + +/** + * Listener for Alfresco-specific transaction callbacks. + * + * @see org.alfresco.repo.transaction.AlfrescoTransactionSupport + * + * @author Derek Hulley + */ +public interface TransactionListener +{ + + /** + * Called before a transaction is committed. + *

+ * All transaction resources are still available. + * + * @param readOnly true if the transaction is read-only + */ + void beforeCommit(boolean readOnly); + + /** + * Invoked before transaction commit/rollback. Will be called after + * {@link #beforeCommit(boolean) } even if {@link #beforeCommit(boolean)} + * failed. + *

+ * All transaction resources are still available. + */ + void beforeCompletion(); + + /** + * Invoked after transaction commit. + *

+ * Any exceptions generated here will only be logged and will have no effect + * on the state of the transaction. + *

+ * Although all transaction resources are still available, this method should + * be used only for cleaning up resources after a commit has occured. + */ + void afterCommit(); + + /** + * Invoked after transaction rollback. + *

+ * Any exceptions generated here will only be logged and will have no effect + * on the state of the transaction. + *

+ * Although all transaction resources are still available, this method should + * be used only for cleaning up resources after a rollback has occured. + */ + void afterRollback(); +} diff --git a/src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java b/src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java new file mode 100644 index 0000000000..efd42e3b17 --- /dev/null +++ b/src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.transaction; + +/** + * NO-OP listener. + * + * @author Derek Hulley + * @since 5.0 + */ +public abstract class TransactionListenerAdapter implements TransactionListener +{ + /** + * {@inheritDoc} + */ + @Override + public void beforeCommit(boolean readOnly) + { + } + + /** + * {@inheritDoc} + */ + @Override + public void beforeCompletion() + { + } + + /** + * {@inheritDoc} + */ + @Override + public void afterCommit() + { + } + + /** + * {@inheritDoc} + */ + @Override + public void afterRollback() + { + } +} diff --git a/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java b/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java new file mode 100644 index 0000000000..e6b2d50066 --- /dev/null +++ b/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java @@ -0,0 +1,639 @@ +/* + * Copyright (C) 2014-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.transaction; + + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedSet; +import java.util.concurrent.ConcurrentSkipListSet; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.GUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.ParameterCheck; +import org.springframework.orm.hibernate3.SessionFactoryUtils; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * Helper class to manage transaction synchronization. This provides helpers to + * ensure that the necessary TransactionSynchronization instances + * are registered on behalf of the application code. + * + * @author mrogers + */ +public abstract class TransactionSupportUtil +{ + private static Log logger = LogFactory.getLog(TransactionSupportUtil.class); + + /** + * The order of synchronization set to be 100 less than the Hibernate synchronization order + */ + public static final int SESSION_SYNCHRONIZATION_ORDER = + SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER - 100; + + /** resource key to store the transaction synchronizer instance */ + protected static final String RESOURCE_KEY_TXN_SYNCH = "txnSynch"; + /** resource binding during after-completion phase */ + protected static final String RESOURCE_KEY_TXN_COMPLETING = "AlfrescoTransactionSupport.txnCompleting"; + + /** + * @return Returns the system time when the transaction started, or -1 if there is no current transaction. + */ + public static long getTransactionStartTime() + { + /* + * This method can be called outside of a transaction, so we can go direct to the synchronizations. + */ + TransactionSynchronizationImpl txnSynch = + (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); + if (txnSynch == null) + { + if (TransactionSynchronizationManager.isSynchronizationActive()) + { + // need to lazily register synchronizations + return registerSynchronizations().getTransactionStartTime(); + } + else + { + return -1; // not in a transaction + } + } + else + { + return txnSynch.getTransactionStartTime(); + } + } + + /** + * Get a unique identifier associated with each transaction of each thread. Null is returned if + * no transaction is currently active. + * + * @return Returns the transaction ID, or null if no transaction is present + */ + public static String getTransactionId() + { + /* + * Go direct to the synchronizations as we don't want to register a resource if one doesn't exist. + * This method is heavily used, so the simple Map lookup on the ThreadLocal is the fastest. + */ + + TransactionSynchronizationImpl txnSynch = + (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); + if (txnSynch == null) + { + if (TransactionSynchronizationManager.isSynchronizationActive()) + { + // need to lazily register synchronizations + return registerSynchronizations().getTransactionId(); + } + else + { + return null; // not in a transaction + } + } + else + { + return txnSynch.getTransactionId(); + } + } + + public static boolean isActualTransactionActive() + { + return TransactionSynchronizationManager.isActualTransactionActive(); + } + + /** + * Gets a resource associated with the current transaction, which must be active. + *

+ * All necessary synchronization instances will be registered automatically, if required. + * + * + * @param key the thread resource map key + * @return Returns a thread resource of null if not present + * + * @see org.alfresco.repo.transaction.TransactionalResourceHelper for helper methods to create and bind common collection types + */ + @SuppressWarnings("unchecked") + public static R getResource(Object key) + { + // get the synchronization + TransactionSynchronizationImpl txnSynch = getSynchronization(); + // get the resource + Object resource = txnSynch.resources.get(key); + // done + if (logger.isTraceEnabled()) + { + logger.trace("Fetched resource: \n" + + " key: " + key + "\n" + + " resource: " + resource); + } + return (R) resource; + } + + /** + * Gets the current transaction synchronization instance, which contains the locally bound + * resources that are available to {@link #getResource(Object) retrieve} or + * {@link #bindResource(Object, Object) add to}. + *

+ * This method also ensures that the transaction binding has been performed. + * + * @return Returns the common synchronization instance used + */ + private static TransactionSynchronizationImpl getSynchronization() + { + // ensure synchronizations + return registerSynchronizations(); + } + + /** + * Binds the Alfresco-specific to the transaction resources + * + * @return Returns the current or new synchronization implementation + */ + private static TransactionSynchronizationImpl registerSynchronizations() + { + /* + * No thread synchronization or locking required as the resources are all threadlocal + */ + if (!TransactionSynchronizationManager.isSynchronizationActive()) + { + Thread currentThread = Thread.currentThread(); + throw new AlfrescoRuntimeException("Transaction must be active and synchronization is required: " + currentThread); + } + TransactionSynchronizationImpl txnSynch = + (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); + if (txnSynch != null) + { + // synchronization already registered + return txnSynch; + } + // we need a unique ID for the transaction + String txnId = GUID.generate(); + // register the synchronization + txnSynch = new TransactionSynchronizationImpl(txnId); + TransactionSynchronizationManager.registerSynchronization(txnSynch); + // register the resource that will ensure we don't duplication the synchronization + TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Bound txn synch: " + txnSynch); + } + return txnSynch; + } + + /** + * Cleans out transaction resources if present + */ + private static void clearSynchronization() + { + if (TransactionSynchronizationManager.hasResource(RESOURCE_KEY_TXN_SYNCH)) + { + Object txnSynch = TransactionSynchronizationManager.unbindResource(RESOURCE_KEY_TXN_SYNCH); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Unbound txn synch:" + txnSynch); + } + } + } + + /** + * Helper method to rebind the synchronization to the transaction + * + * @param txnSynch TransactionSynchronizationImpl + */ + private static void rebindSynchronization(TransactionSynchronizationImpl txnSynch) + { + TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); + if (logger.isDebugEnabled()) + { + logger.debug("Bound (rebind) txn synch: " + txnSynch); + } + } + + /** + * Binds a resource to the current transaction, which must be active. + *

+ * All necessary synchronization instances will be registered automatically, if required. + * + * @param key Object + * @param resource Object + */ + public static void bindResource(Object key, Object resource) + { + // get the synchronization + TransactionSynchronizationImpl txnSynch = getSynchronization(); + // bind the resource + txnSynch.resources.put(key, resource); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Bound resource: \n" + + " key: " + key + "\n" + + " resource: " + resource); + } + } + + /** + * Unbinds a resource from the current transaction, which must be active. + *

+ * All necessary synchronization instances will be registered automatically, if required. + * + * @param key Object + */ + public static void unbindResource(Object key) + { + // get the synchronization + TransactionSynchronizationImpl txnSynch = getSynchronization(); + // remove the resource + txnSynch.resources.remove(key); + // done + if (logger.isDebugEnabled()) + { + logger.debug("Unbound resource: \n" + + " key: " + key); + } + } + + /** + * Bind listener to the specified priority. Duplicate bindings + * + * The priority specifies the position for the listener during commit. + * For example flushing of caches needs to happen very late. + * @param listener the listener to bind. + * @param priority 0 = Normal priority + * @return true if the new listener was bound. False if the listener was already bound. + */ + public static boolean bindListener(TransactionListener listener, int priority) + { + if (logger.isDebugEnabled()) + { + logger.debug("Bind Listener listener: " + listener + ", priority: " + priority); + } + TransactionSynchronizationImpl synch = getSynchronization(); + return synch.addListener(listener, priority); + } + + /** + * @return Returns all the listeners in a list disconnected from the original set + */ + public static Set getListeners() + { + // get the synchronization + TransactionSynchronizationImpl txnSynch = getSynchronization(); + + return txnSynch.getListenersIterable(); + + } + + /** + * Handler of txn synchronization callbacks specific to internal + * application requirements + */ + private static class TransactionSynchronizationImpl extends TransactionSynchronizationAdapter + { + private long txnStartTime; + private final String txnId; + private final Map resources; + + /** + * priority to listeners + */ + private final Map>priorityLookup = new HashMap>(); + + /** + * Sets up the resource map + * + * @param txnId String + */ + public TransactionSynchronizationImpl(String txnId) + { + this.txnStartTime = System.currentTimeMillis(); + this.txnId = txnId; + priorityLookup.put(0, new LinkedHashSet(5)); + resources = new HashMap(17); + } + + public long getTransactionStartTime() + { + return txnStartTime; + } + + public String getTransactionId() + { + return txnId; + } + + /** + * Add a trasaction listener + * + * @return true if the listener was added, false if it already existed. + */ + public boolean addListener(TransactionListener listener, int priority) + { + ParameterCheck.mandatory("listener", listener); + + if(this.priorityLookup.containsKey(priority)) + { + Set listeners = priorityLookup.get(priority); + return listeners.add(listener); + } + else + { + synchronized (priorityLookup) + { + if(priorityLookup.containsKey(priority)) + { + Set listeners = priorityLookup.get(priority); + return listeners.add(listener); + } + else + { + Set listeners = new LinkedHashSet(5); + priorityLookup.put(priority, listeners); + return listeners.add(listener); + } + } + } + } + + /** + * Return the level zero (normal) listeners + * + * @return Returns the level zero (normal) listeners in a list disconnected from the original set + */ + private List getLevelZeroListenersIterable() + { + Setlisteners = priorityLookup.get(0); + return new ArrayList(listeners); + } + + /** + * @return all the listeners regardless of priority + */ + private Set getListenersIterable() + { + Set ret = new LinkedHashSet(); + Set>> entries = priorityLookup.entrySet(); + + for(Entry> entry : entries) + { + ret.addAll((Set)entry.getValue()); + } + + return ret; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(50); + sb.append("TransactionSychronizationImpl") + .append("[ txnId=").append(txnId) + .append("]"); + return sb.toString(); + } + + /** + * @see org.alfresco.repo.transaction.AlfrescoTransactionSupport#SESSION_SYNCHRONIZATION_ORDER + */ + @Override + public int getOrder() + { + return TransactionSupportUtil.SESSION_SYNCHRONIZATION_ORDER; + } + + @Override + public void suspend() + { + if (logger.isDebugEnabled()) + { + logger.debug("Suspending transaction: " + this); + } + TransactionSupportUtil.clearSynchronization(); + } + + @Override + public void resume() + { + if (logger.isDebugEnabled()) + { + logger.debug("Resuming transaction: " + this); + } + TransactionSupportUtil.rebindSynchronization(this); + } + + /** + * Pre-commit cleanup. + *

+ * Ensures that the session transaction listeners are property executed. + * + * The Lucene indexes are then prepared. + */ + @Override + public void beforeCommit(boolean readOnly) + { + if (logger.isDebugEnabled()) + { + logger.debug("Before commit " + (readOnly ? "read-only" : "" ) + this); + } + // get the txn ID + TransactionSynchronizationImpl synch = (TransactionSynchronizationImpl) + TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); + if (synch == null) + { + throw new AlfrescoRuntimeException("No synchronization bound to thread"); + } + + logger.trace("Before Prepare - level 0"); + + // Run the priority 0 (normal) listeners + // These are still considered part of the transaction so are executed here + doBeforeCommit(readOnly); + + // Now run the > 0 listeners beforeCommit + Set priorities = priorityLookup.keySet(); + + SortedSet sortedPriorities = new ConcurrentSkipListSet(FORWARD_INTEGER_ORDER); + sortedPriorities.addAll(priorities); + sortedPriorities.remove(0); // already done level 0 above + + if(logger.isDebugEnabled()) + { + logger.debug("Before Prepare priorities:" + sortedPriorities); + } + for(Integer priority : sortedPriorities) + { + Set listeners = priorityLookup.get(priority); + + for(TransactionListener listener : listeners) + { + listener.beforeCommit(readOnly); + } + } + if(logger.isDebugEnabled()) + { + logger.debug("Prepared"); + } + } + + /** + * Execute the beforeCommit event handlers for the registered listeners + * + * @param readOnly is read only + */ + private void doBeforeCommit(boolean readOnly) + { + doBeforeCommit(new HashSet(), readOnly); + } + + /** + * Executes the beforeCommit event handlers for the outstanding listeners. + * This process is iterative as the process of calling listeners may lead to more listeners + * being added. The new listeners will be processed until there no listeners remaining. + * + * @param visitedListeners a set containing the already visited listeners + * @param readOnly is read only + */ + private void doBeforeCommit(Set visitedListeners, boolean readOnly) + { + Set listeners = priorityLookup.get(0); + Set pendingListeners = new HashSet(listeners); + pendingListeners.removeAll(visitedListeners); + + if (pendingListeners.size() != 0) + { + for (TransactionListener listener : pendingListeners) + { + listener.beforeCommit(readOnly); + visitedListeners.add(listener); + } + + doBeforeCommit(visitedListeners, readOnly); + } + } + + @Override + public void beforeCompletion() + { + if (logger.isDebugEnabled()) + { + logger.debug("Before completion: " + this); + } + // notify listeners + for (TransactionListener listener : getLevelZeroListenersIterable()) + { + listener.beforeCompletion(); + } + } + + + @Override + public void afterCompletion(int status) + { + String statusStr = "unknown"; + switch (status) + { + case TransactionSynchronization.STATUS_COMMITTED: + statusStr = "committed"; + break; + case TransactionSynchronization.STATUS_ROLLED_BACK: + statusStr = "rolled-back"; + break; + default: + } + if (logger.isDebugEnabled()) + { + logger.debug("After completion (" + statusStr + "): " + this); + } + + // Force any queries for read-write state to return TXN_READ_ONLY + // This will be cleared with the synchronization, so we don't need to clear it out + TransactionSupportUtil.bindResource(RESOURCE_KEY_TXN_COMPLETING, Boolean.TRUE); + + Set priorities = priorityLookup.keySet(); + + SortedSet sortedPriorities = new ConcurrentSkipListSet(REVERSE_INTEGER_ORDER); + sortedPriorities.addAll(priorities); + + // Need to run these in reverse order cache,lucene,listeners + for(Integer priority : sortedPriorities) + { + Set listeners = new HashSet(priorityLookup.get(priority)); + + for(TransactionListener listener : listeners) + { + try + { + if (status == TransactionSynchronization.STATUS_COMMITTED) + { + listener.afterCommit(); + } + else + { + listener.afterRollback(); + } + } + catch (RuntimeException e) + { + logger.error("After completion (" + statusStr + ") TransactionalCache exception", e); + } + } + } + if(logger.isDebugEnabled()) + { + logger.debug("After Completion: DONE"); + } + + + // clear the thread's registrations and synchronizations + TransactionSupportUtil.clearSynchronization(); + } + } + + static private Comparator FORWARD_INTEGER_ORDER = new Comparator() + { + @Override + public int compare(Integer arg0, Integer arg1) + { + return arg0.intValue() - arg1.intValue(); + } + } ; + + static private Comparator REVERSE_INTEGER_ORDER = new Comparator() + { + @Override + public int compare(Integer arg0, Integer arg1) + { + return arg1.intValue() - arg0.intValue(); + } + } ; + +} diff --git a/src/main/java/org/alfresco/util/xml/SchemaHelper.java b/src/main/java/org/alfresco/util/xml/SchemaHelper.java new file mode 100644 index 0000000000..65ae33fd31 --- /dev/null +++ b/src/main/java/org/alfresco/util/xml/SchemaHelper.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.xml; + +import java.io.File; +import java.net.URL; + +import org.springframework.util.ResourceUtils; +import org.xml.sax.InputSource; +import org.xml.sax.SAXParseException; + +import com.sun.codemodel.JCodeModel; +import com.sun.tools.xjc.api.ErrorListener; +import com.sun.tools.xjc.api.S2JJAXBModel; +import com.sun.tools.xjc.api.SchemaCompiler; +import com.sun.tools.xjc.api.XJC; + +/** + * Helper to generate code from XSD files. + * + * @author Derek Hulley + * @since 3.2 + */ +public class SchemaHelper +{ + public static void main(String ... args) + { + if (args.length < 2 || !args[0].startsWith("--compile-xsd=") && !args[1].startsWith("--output-dir=")) + { + System.out.println("Usage: SchemaHelper --compile-xsd= --output-dir="); + System.exit(1); + } + final String urlStr = args[0].substring(14); + if (urlStr.length() == 0) + { + System.out.println("Usage: SchemaHelper --compile-xsd= --output-dir="); + System.exit(1); + } + final String dirStr = args[1].substring(13); + if (dirStr.length() == 0) + { + System.out.println("Usage: SchemaHelper --compile-xsd= --output-dir="); + System.exit(1); + } + try + { + URL url = ResourceUtils.getURL(urlStr); + File dir = new File(dirStr); + if (!dir.exists() || !dir.isDirectory()) + { + System.out.println("Output directory not found: " + dirStr); + System.exit(1); + } + + ErrorListener errorListener = new ErrorListener() + { + public void warning(SAXParseException e) + { + System.out.println("WARNING: " + e.getMessage()); + } + public void info(SAXParseException e) + { + System.out.println("INFO: " + e.getMessage()); + } + public void fatalError(SAXParseException e) + { + handleException(urlStr, e); + } + public void error(SAXParseException e) + { + handleException(urlStr, e); + } + }; + + SchemaCompiler compiler = XJC.createSchemaCompiler(); + compiler.setErrorListener(errorListener); + compiler.parseSchema(new InputSource(url.toExternalForm())); + S2JJAXBModel model = compiler.bind(); + if (model == null) + { + System.out.println("Failed to produce binding model for URL " + urlStr); + System.exit(1); + } + JCodeModel codeModel = model.generateCode(null, errorListener); + codeModel.build(dir); + } + catch (Throwable e) + { + handleException(urlStr, e); + System.exit(1); + } + } + private static void handleException(String urlStr, Throwable e) + { + System.out.println("Error processing XSD " + urlStr); + e.printStackTrace(); + } +} diff --git a/src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java b/src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java new file mode 100644 index 0000000000..0a77cdfcea --- /dev/null +++ b/src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.web.scripts.servlet; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +/** + * Simple servlet filter to add a 'Cache-Control' HTTP header to a response. + * The Cache-Control header is set to a max-age value by a configurable setting + * in the 'expires' init parameters - values are in days. + * + * WebScripts or other servlets that happen to match the response type + * configured for the filter (e.g. "*.js") should override cache settings + * as required. + * + * @author Kevin Roast + */ +public class StaticAssetCacheFilter implements Filter +{ + private static final long DAY_S = 60L*60L*24L; // 1 day in seconds + private static final long DEFAULT_30DAYS = 30L; // default of 30 days if not configured + + private long expire = DAY_S * DEFAULT_30DAYS; // initially set to default value of 30 days + + + /* (non-Javadoc) + * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) + */ + public void init(FilterConfig config) throws ServletException + { + String expireParam = config.getInitParameter("expires"); + if (expireParam != null) + { + this.expire = Long.parseLong(expireParam) * DAY_S; + } + } + + /* (non-Javadoc) + * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) + */ + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, + ServletException + { + ((HttpServletResponse)res).setHeader("Cache-Control", "must-revalidate, max-age=" + Long.toString(this.expire)); + chain.doFilter(req, res); + } + + /* (non-Javadoc) + * @see javax.servlet.Filter#destroy() + */ + public void destroy() + { + this.expire = DAY_S * DEFAULT_30DAYS; + } +} \ No newline at end of file diff --git a/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java b/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java new file mode 100644 index 0000000000..11ddffdcdc --- /dev/null +++ b/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java @@ -0,0 +1,326 @@ +/* +* Copyright (C) 2005-2013 Alfresco Software Limited. +* +* This file is part of Alfresco +* +* Alfresco is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Alfresco is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with Alfresco. If not, see . +*/ + +package org.alfresco.web.scripts.servlet; + +import javax.management.*; +import javax.security.auth.x500.X500Principal; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * The X509ServletFilterBase enforces X509 Authentication. + * + * Optional Init Param: + * cert-contains : Ensure that the principal subject of the cert contains a specific string. + * + * The X509ServletFilter will also ensure that the cert is present in the request, which will only happen if there + * is a successful SSL handshake which includes client authentication. This handshake is handled by the Application Server. + * A SSL handshake that does not include client Authentication will receive a 403 error response. + * + * The checkInforce method must be implemented to determine if the X509 Authentication is turned on. This allows + * applications to turn on/off X509 Authentication based on parameters outside of the web.xml. + * + * */ + +public abstract class X509ServletFilterBase implements Filter +{ + + protected boolean enforce; + private String httpsPort; + private String certContains; + private static Log logger = LogFactory.getLog(X509ServletFilterBase.class); + + public void init(FilterConfig config) throws ServletException + { + try + { + /* + * Find out if we are enforcing. + */ + + if(logger.isDebugEnabled()) + { + logger.debug("Initializing X509ServletFilter"); + } + + this.handleClientAuth(); + + this.enforce = checkEnforce(config.getServletContext()); + + if(logger.isDebugEnabled()) + { + logger.debug("Enforcing X509 Authentication:"+this.enforce); + } + + if (this.enforce) + { + /* + * We are enforcing so get the cert-contains string. + */ + + this.certContains = config.getInitParameter("cert-contains"); + + if(logger.isDebugEnabled()) + { + if(certContains == null) + { + logger.debug("Not enforcing cert-contains"); + } + else + { + logger.debug("Enforcing cert-contains:" + this.certContains); + } + } + } + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + public void setHttpsPort(int port) + { + this.httpsPort = Integer.toString(port); + } + + public void doFilter(ServletRequest request, + ServletResponse response, + FilterChain chain) throws IOException, + ServletException + { + + HttpServletRequest httpRequest = (HttpServletRequest)request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + + /* + * Test if we are enforcing X509. + */ + if(this.enforce) + { + if(logger.isDebugEnabled()) + { + logger.debug("Enforcing X509 request"); + } + + X509Certificate[] certs = (X509Certificate[])httpRequest.getAttribute("javax.servlet.request.X509Certificate"); + if(validCert(certs)) + { + + if(logger.isDebugEnabled()) + { + logger.debug("Cert is valid"); + } + + /* + * The cert is valid so forward the request. + */ + + chain.doFilter(request,response); + } + else + { + if(logger.isDebugEnabled()) + { + logger.debug("Cert is invalid"); + } + + if(!httpRequest.isSecure()) + { + if(this.httpsPort != null) + { + String redirectUrl = httpRequest.getRequestURL().toString(); + int port = httpRequest.getLocalPort(); + String httpPort = Integer.toString(port); + redirectUrl = redirectUrl.replace(httpPort, httpsPort); + redirectUrl = redirectUrl.replace("http", "https"); + String query = httpRequest.getQueryString(); + if(query != null) + { + redirectUrl = redirectUrl+"?"+query; + } + + if(logger.isDebugEnabled()) + { + logger.debug("Redirecting to:"+redirectUrl); + } + httpResponse.sendRedirect(redirectUrl); + return; + } + } + /* + * Invalid cert so send 403. + */ + httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "X509 Authentication failure"); + } + } + else + { + /* + * We are not enforcing X509 so forward the request + */ + chain.doFilter(request,response); + } + } + + + /** + * + * @param servletContext + * @return true if enforcing X509 false if not enforcing X509 + * @throws IOException + * + * The checkInforce method is called during the initialization of the Filter. Implement this method to decide if + * X509 security is being enforced. + * + **/ + + protected abstract boolean checkEnforce(ServletContext servletContext) throws IOException; + + private boolean validCert(X509Certificate[] certs) + { + /* + * If the cert is null then the it's not valid. + */ + + if(certs == null) + { + return false; + } + + /* + * Get the first certificate in the chain. The first certificate is the client certificate. + */ + + X509Certificate cert = certs[0]; + try + { + /* + * check the certificate has not expired. + */ + if(logger.isDebugEnabled()) + { + logger.debug("Checking cert is valid"); + } + cert.checkValidity(); + } + catch (Exception e) + { + logger.error("Cert is invalid", e); + return false; + } + + X500Principal x500Principal = cert.getSubjectX500Principal(); + String name = x500Principal.getName(); + + /* + * Cert contains is an optional check + */ + + if(this.certContains == null) + { + return true; + } + + /* + * Check that the cert contains the specified value. + */ + + if(name.contains(this.certContains)) + { + if(logger.isDebugEnabled()) + { + logger.debug("Cert: "+ name + " contains: "+ this.certContains); + } + + return true; + } + else + { + logger.error("Cert: " + name + " does not contain: " + this.certContains); + return false; + } + } + + public void destroy() + { + } + + private void handleClientAuth() + { + try + { + MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0); + + //Are we Tomcat + ObjectName catalina = new ObjectName("Catalina", "type", "Engine"); + Set objectNames = mBeanServer.queryNames(catalina, null); + if(objectNames == null || objectNames.size() == 0) + { + //We do not appear to be Tomcat + return; + } + + //We are Tomcat so look for the clientAuth + QueryExp query = Query.or(Query.eq(Query.attr("clientAuth"), Query.value("want")), + Query.eq(Query.attr("clientAuth"), Query.value(true))); + + objectNames = mBeanServer.queryNames(null, query); + + if (objectNames != null && objectNames.size() == 0) + { + logger.warn("clientAuth does not appear to be set for Tomcat. clientAuth must be set to 'want' for X509 Authentication"); + logger.warn("Attempting to set clientAuth=want through JMX..."); + + query = Query.eq(Query.attr("secure"), Query.value(true)); + + Set objectNames1 = mBeanServer.queryNames(null, query); + + if(objectNames1 != null) + { + for(ObjectName objectName : objectNames1) + { + if(objectName.toString().contains("ProtocolHandler")) + { + logger.warn("Setting clientAuth=want on MBean:" + objectName.toString()); + mBeanServer.setAttribute(objectName, new Attribute("clientAuth", "want")); + return; + } + } + } + + logger.warn("Unable to set clientAuth=want through JMX."); + } + } + catch(Throwable t) + { + logger.warn("An error occurred while checking for clientAuth. Turn on debug logging to see the stacktrace."); + logger.debug("Error while handling clientAuth",t); + } + } +} \ No newline at end of file diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties new file mode 100644 index 0000000000..b4a8ca66bf --- /dev/null +++ b/src/main/resources/log4j.properties @@ -0,0 +1,264 @@ +# Set root logger level to error +log4j.rootLogger=error, Console, File + +###### Console appender definition ####### + +# All outputs currently set to be a ConsoleAppender. +log4j.appender.Console=org.apache.log4j.ConsoleAppender +log4j.appender.Console.layout=org.apache.log4j.PatternLayout + +# use log4j NDC to replace %x with tenant domain / username +log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} %x %-5p [%c{3}] [%t] %m%n +#log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%c] %m%n + +###### File appender definition ####### +log4j.appender.File=org.apache.log4j.DailyRollingFileAppender +log4j.appender.File.File=alfresco.log +log4j.appender.File.Append=true +log4j.appender.File.DatePattern='.'yyyy-MM-dd +log4j.appender.File.layout=org.apache.log4j.PatternLayout +log4j.appender.File.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} %-5p [%c] [%t] %m%n + +###### Hibernate specific appender definition ####### +#log4j.appender.file=org.apache.log4j.FileAppender +#log4j.appender.file.File=hibernate.log +#log4j.appender.file.layout=org.apache.log4j.PatternLayout +#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n + +###### Log level overrides ####### + +# Commented-in loggers will be exposed as JMX MBeans (refer to org.alfresco.repo.admin.Log4JHierarchyInit) +# Hence, generally useful loggers should be listed with at least ERROR level to allow simple runtime +# control of the level via a suitable JMX Console. Also, any other loggers can be added transiently via +# Log4j addLoggerMBean as long as the logger exists and has been loaded. + +# Hibernate +log4j.logger.org.hibernate=error +log4j.logger.org.hibernate.util.JDBCExceptionReporter=fatal +log4j.logger.org.hibernate.event.def.AbstractFlushingEventListener=fatal +log4j.logger.org.hibernate.type=warn +log4j.logger.org.hibernate.cfg.SettingsFactory=warn + +# Spring +log4j.logger.org.springframework=warn +# Turn off Spring remoting warnings that should really be info or debug. +log4j.logger.org.springframework.remoting.support=error +log4j.logger.org.springframework.util=error + +# Axis/WSS4J +log4j.logger.org.apache.axis=info +log4j.logger.org.apache.ws=info + +# CXF +log4j.logger.org.apache.cxf=error + +# MyFaces +log4j.logger.org.apache.myfaces.util.DebugUtils=info +log4j.logger.org.apache.myfaces.el.VariableResolverImpl=error +log4j.logger.org.apache.myfaces.application.jsp.JspViewHandlerImpl=error +log4j.logger.org.apache.myfaces.taglib=error + +# OpenOfficeConnection +log4j.logger.net.sf.jooreports.openoffice.connection=fatal + +# log prepared statement cache activity ### +log4j.logger.org.hibernate.ps.PreparedStatementCache=info + +# Alfresco +log4j.logger.org.alfresco=error +log4j.logger.org.alfresco.repo.admin=info +log4j.logger.org.alfresco.repo.transaction=warn +log4j.logger.org.alfresco.repo.cache.TransactionalCache=warn +log4j.logger.org.alfresco.repo.model.filefolder=warn +log4j.logger.org.alfresco.repo.tenant=info +log4j.logger.org.alfresco.config=warn +log4j.logger.org.alfresco.config.JndiObjectFactoryBean=warn +log4j.logger.org.alfresco.config.JBossEnabledWebApplicationContext=warn +log4j.logger.org.alfresco.repo.management.subsystems=warn +log4j.logger.org.alfresco.repo.management.subsystems.ChildApplicationContextFactory=info +log4j.logger.org.alfresco.repo.management.subsystems.ChildApplicationContextFactory$ChildApplicationContext=warn +log4j.logger.org.alfresco.repo.security.sync=info +log4j.logger.org.alfresco.repo.security.person=info + +log4j.logger.org.alfresco.sample=info +log4j.logger.org.alfresco.web=info +#log4j.logger.org.alfresco.web.app.AlfrescoNavigationHandler=debug +#log4j.logger.org.alfresco.web.ui.repo.component.UIActions=debug +#log4j.logger.org.alfresco.web.ui.repo.tag.PageTag=debug +#log4j.logger.org.alfresco.web.bean.clipboard=debug +log4j.logger.org.alfresco.service.descriptor.DescriptorService=info +#log4j.logger.org.alfresco.web.page=debug + +log4j.logger.org.alfresco.repo.importer.ImporterBootstrap=error +#log4j.logger.org.alfresco.repo.importer.ImporterBootstrap=info + +log4j.logger.org.alfresco.repo.admin.patch.PatchExecuter=info +log4j.logger.org.alfresco.repo.domain.patch.ibatis.PatchDAOImpl=info + +# Specific patches +log4j.logger.org.alfresco.repo.admin.patch.impl.DeploymentMigrationPatch=info +log4j.logger.org.alfresco.repo.version.VersionMigrator=info + +log4j.logger.org.alfresco.repo.module.ModuleServiceImpl=info +log4j.logger.org.alfresco.repo.domain.schema.SchemaBootstrap=info +log4j.logger.org.alfresco.repo.admin.ConfigurationChecker=info +log4j.logger.org.alfresco.repo.node.index.AbstractReindexComponent=warn +log4j.logger.org.alfresco.repo.node.index.IndexTransactionTracker=warn +log4j.logger.org.alfresco.repo.node.index.FullIndexRecoveryComponent=info +log4j.logger.org.alfresco.util.OpenOfficeConnectionTester=info +log4j.logger.org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl=warn +log4j.logger.org.alfresco.repo.domain.hibernate.DirtySessionMethodInterceptor=warn +log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=warn +log4j.logger.org.alfresco.util.transaction.SpringAwareUserTransaction.trace=warn +log4j.logger.org.alfresco.util.AbstractTriggerBean=warn +log4j.logger.org.alfresco.enterprise.repo.cluster=info +log4j.logger.org.alfresco.repo.version.Version2ServiceImpl=warn + +#log4j.logger.org.alfresco.web.app.DebugPhaseListener=debug +log4j.logger.org.alfresco.repo.node.db.NodeStringLengthWorker=info + +log4j.logger.org.alfresco.repo.workflow=info + +# CIFS server debugging +log4j.logger.org.alfresco.smb.protocol=error +#log4j.logger.org.alfresco.smb.protocol.auth=debug +#log4j.logger.org.alfresco.acegi=debug + +# FTP server debugging +log4j.logger.org.alfresco.ftp.protocol=error +#log4j.logger.org.alfresco.ftp.server=debug + +# WebDAV debugging +#log4j.logger.org.alfresco.webdav.protocol=debug +log4j.logger.org.alfresco.webdav.protocol=info + +# NTLM servlet filters +#log4j.logger.org.alfresco.web.app.servlet.NTLMAuthenticationFilter=debug +#log4j.logger.org.alfresco.repo.webdav.auth.NTLMAuthenticationFilter=debug + +# Kerberos servlet filters +#log4j.logger.org.alfresco.web.app.servlet.KerberosAuthenticationFilter=debug +#log4j.logger.org.alfresco.repo.webdav.auth.KerberosAuthenticationFilter=debug + +# File servers +log4j.logger.org.alfresco.fileserver=warn + +# Repo filesystem debug logging +#log4j.logger.org.alfresco.filesys.repo.ContentDiskDriver=debug + +# Integrity message threshold - if 'failOnViolation' is off, then WARNINGS are generated +log4j.logger.org.alfresco.repo.node.integrity=ERROR + +# Indexer debugging +log4j.logger.org.alfresco.repo.search.Indexer=error +#log4j.logger.org.alfresco.repo.search.Indexer=debug + +log4j.logger.org.alfresco.repo.search.impl.lucene.index=error +log4j.logger.org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexerImpl=warn +#log4j.logger.org.alfresco.repo.search.impl.lucene.index=DEBUG + +# Audit debugging +# log4j.logger.org.alfresco.repo.audit=DEBUG +# log4j.logger.org.alfresco.repo.audit.model=DEBUG + +# Property sheet and modelling debugging +# change to error to hide the warnings about missing properties and associations +log4j.logger.alfresco.missingProperties=warn + +# Dictionary/Model debugging +log4j.logger.org.alfresco.repo.dictionary=warn +log4j.logger.org.alfresco.repo.dictionary.types.period=warn + +# Virtualization Server Registry +log4j.logger.org.alfresco.mbeans.VirtServerRegistry=error + +# Spring context runtime property setter +log4j.logger.org.alfresco.util.RuntimeSystemPropertiesSetter=info + +# Debugging options for clustering +log4j.logger.org.alfresco.repo.content.ReplicatingContentStore=error +log4j.logger.org.alfresco.repo.content.replication=error + +#log4j.logger.org.alfresco.repo.deploy.DeploymentServiceImpl=debug + +# Activity service +log4j.logger.org.alfresco.repo.activities=warn + +# User usage tracking +log4j.logger.org.alfresco.repo.usage=info + +# Sharepoint +log4j.logger.org.alfresco.module.vti=info + +# Forms Engine +log4j.logger.org.alfresco.web.config.forms=info +log4j.logger.org.alfresco.web.scripts.forms=info + +# CMIS +log4j.logger.org.alfresco.opencmis=error +log4j.logger.org.alfresco.opencmis.AlfrescoCmisServiceInterceptor=error +log4j.logger.org.alfresco.cmis=error +log4j.logger.org.alfresco.cmis.dictionary=warn +log4j.logger.org.apache.chemistry.opencmis=info +log4j.logger.org.apache.chemistry.opencmis.server.impl.browser.CmisBrowserBindingServlet=OFF +log4j.logger.org.apache.chemistry.opencmis.server.impl.atompub.CmisAtomPubServlet=OFF + +# IMAP +log4j.logger.org.alfresco.repo.imap=info + +# JBPM +# Note: non-fatal errors (eg. logged during job execution) should be handled by Alfresco's retrying transaction handler +log4j.logger.org.jbpm.graph.def.GraphElement=fatal + +#log4j.logger.org.alfresco.repo.googledocs=debug + +###### Scripting ####### + +# Web Framework +log4j.logger.org.springframework.extensions.webscripts=info +log4j.logger.org.springframework.extensions.webscripts.ScriptLogger=warn +log4j.logger.org.springframework.extensions.webscripts.ScriptDebugger=off + +# Repository +log4j.logger.org.alfresco.repo.web.scripts=warn +log4j.logger.org.alfresco.repo.web.scripts.BaseWebScriptTest=info +log4j.logger.org.alfresco.repo.web.scripts.AlfrescoRhinoScriptDebugger=off +log4j.logger.org.alfresco.repo.jscript=error +log4j.logger.org.alfresco.repo.jscript.ScriptLogger=warn +log4j.logger.org.alfresco.repo.cmis.rest.CMISTest=info + +log4j.logger.org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImpl=off +log4j.logger.org.alfresco.repo.domain.schema.script.ScriptExecutorImpl=info + +log4j.logger.org.alfresco.repo.search.impl.solr.facet.SolrFacetServiceImpl=info + +# Bulk Filesystem Import Tool +log4j.logger.org.alfresco.repo.bulkimport=warn + +# Freemarker +# Note the freemarker.runtime logger is used to log non-fatal errors that are handled by Alfresco's retrying transaction handler +log4j.logger.freemarker.runtime= + +# Metadata extraction +log4j.logger.org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter=warn + +# Reduces PDFont error level due to ALF-7105 +log4j.logger.org.apache.pdfbox.pdmodel.font.PDSimpleFont=fatal +log4j.logger.org.apache.pdfbox.pdmodel.font.PDFont=fatal +log4j.logger.org.apache.pdfbox.pdmodel.font.PDCIDFont=fatal + +# no index support +log4j.logger.org.alfresco.repo.search.impl.noindex.NoIndexIndexer=fatal +log4j.logger.org.alfresco.repo.search.impl.noindex.NoIndexSearchService=fatal + +# lucene index warnings +log4j.logger.org.alfresco.repo.search.impl.lucene.index.IndexInfo=warn + +# Warn about RMI socket bind retries. +log4j.logger.org.alfresco.util.remote.server.socket.HostConfigurableSocketFactory=warn + +log4j.logger.org.alfresco.repo.usage.RepoUsageMonitor=info + +# Authorization +log4j.logger.org.alfresco.enterprise.repo.authorization.AuthorizationService=info +log4j.logger.org.alfresco.enterprise.repo.authorization.AuthorizationsConsistencyMonitor=warn diff --git a/src/main/resources/logging.properties b/src/main/resources/logging.properties new file mode 100644 index 0000000000..9e65a1a5ac --- /dev/null +++ b/src/main/resources/logging.properties @@ -0,0 +1,8 @@ +handlers = java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = FINE +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter + +org.activiti.level = FATAL +#Uncomment this to log schema creation statements from Activiti +#org.activiti.engine.impl.db.DbSqlSession.level = FINE diff --git a/src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java b/src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java new file mode 100644 index 0000000000..4d91bc75f0 --- /dev/null +++ b/src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.config; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * @see SystemPropertiesSetterBean + * + * @author Derek Hulley + */ +public class SystemPropertiesSetterBeanTest extends TestCase +{ + private static final String KEY_A = "SystemPropertiesSetterBeanTest.A"; + private static final String KEY_B = "SystemPropertiesSetterBeanTest.B"; + private static final String KEY_C = "SystemPropertiesSetterBeanTest.C"; + private static final String KEY_EXISTING = "SystemPropertiesSetterBeanTest.EXISTING "; + private static final String KEY_PLACEHOLDER = "SystemPropertiesSetterBeanTest.PLACEHOLDER"; + private static final String KEY_EMPTY_STRING = "SystemPropertiesSetterBeanTest.EMPTY_STRING"; + private static final String KEY_NULL = "SystemPropertiesSetterBeanTest.NULL"; + private static final String VALUE_A = "A"; + private static final String VALUE_B = "B"; + private static final String VALUE_C = "C"; + private static final String VALUE_EXISTING = "EXISTING"; + private static final String VALUE_PLACEHOLDER = "${OOPS}"; + private static final String VALUE_EMPTY_STRING = ""; + private static final String VALUE_NULL = null; + + SystemPropertiesSetterBean setter; + private Map propertyMap; + + public void setUp() throws Exception + { + System.setProperty(KEY_EXISTING, VALUE_EXISTING); + + propertyMap = new HashMap(7); + propertyMap.put(KEY_A, VALUE_A); + propertyMap.put(KEY_B, VALUE_B); + propertyMap.put(KEY_C, VALUE_C); + propertyMap.put(KEY_EXISTING, "SHOULD NOT HAVE OVERRIDDEN EXISTING PROPERTY"); + propertyMap.put(KEY_PLACEHOLDER, VALUE_PLACEHOLDER); + propertyMap.put(KEY_EMPTY_STRING, VALUE_EMPTY_STRING); + propertyMap.put(KEY_NULL, VALUE_NULL); + + setter = new SystemPropertiesSetterBean(); + setter.setPropertyMap(propertyMap); + } + + public void testSetUp() + { + assertEquals(VALUE_EXISTING, System.getProperty(KEY_EXISTING)); + assertNull(System.getProperty(KEY_A)); + assertNull(System.getProperty(KEY_B)); + assertNull(System.getProperty(KEY_C)); + } + + public void testSettingOfSystemProperties() + { + setter.init(); + // Check + assertEquals(VALUE_A, System.getProperty(KEY_A)); + assertEquals(VALUE_B, System.getProperty(KEY_B)); + assertEquals(VALUE_C, System.getProperty(KEY_C)); + assertEquals(VALUE_EXISTING, System.getProperty(KEY_EXISTING)); + assertNull("Property placeholder not detected", System.getProperty(KEY_PLACEHOLDER)); + assertNull("Empty string not detected", System.getProperty(KEY_EMPTY_STRING)); + assertNull("Null string not detected", System.getProperty(KEY_NULL)); + } +} diff --git a/src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java b/src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java new file mode 100644 index 0000000000..9a22b69a75 --- /dev/null +++ b/src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2005-2010 Alfresco Software, Ltd. All rights reserved. + * + * License rights for this program may be obtained from Alfresco Software, Ltd. + * pursuant to a written agreement and any use of this program without such an + * agreement is prohibited. + */ +package org.alfresco.encryption; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.util.Arrays; + +import junit.framework.TestCase; + +/** + * Tests that the EncryptingOutputStream and EncryptingInputStream classes work correctly. + */ +public class EncryptingOutputStreamTest extends TestCase +{ + + /** + * Tests encryption / decryption by attempting to encrypt and decrypt the bytes forming this class definition and + * comparing it with the unencrypted bytes. + * + * @throws Exception + * an exception + */ + public void testEncrypt() throws Exception + { + final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + final SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + final byte[] seed = getClass().getName().getBytes("UTF-8"); + random.setSeed(seed); + keyGen.initialize(1024, random); + final KeyPair pair = keyGen.generateKeyPair(); + + final ByteArrayOutputStream buff = new ByteArrayOutputStream(2048); + final OutputStream encrypting = new EncryptingOutputStream(buff, pair.getPublic(), random); + final ByteArrayOutputStream plainText1 = new ByteArrayOutputStream(2048); + + final InputStream in = getClass().getResourceAsStream(getClass().getSimpleName() + ".class"); + final byte[] inbuff = new byte[17]; + int bytesRead; + while ((bytesRead = in.read(inbuff)) != -1) + { + encrypting.write(inbuff, 0, bytesRead); + plainText1.write(inbuff, 0, bytesRead); + } + in.close(); + encrypting.close(); + plainText1.close(); + final InputStream decrypting = new DecryptingInputStream(new ByteArrayInputStream(buff.toByteArray()), pair + .getPrivate()); + final ByteArrayOutputStream plainText2 = new ByteArrayOutputStream(2048); + while ((bytesRead = decrypting.read(inbuff)) != -1) + { + plainText2.write(inbuff, 0, bytesRead); + } + + assertTrue(Arrays.equals(plainText1.toByteArray(), plainText2.toByteArray())); + + } +} diff --git a/src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java b/src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java new file mode 100644 index 0000000000..76a228a3c0 --- /dev/null +++ b/src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.error; + +import java.net.URL; +import java.util.Locale; +import java.util.ResourceBundle; + +import junit.framework.TestCase; + +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Alfresco runtime exception test + * + * @author Roy Wetherall + */ +public class AlfrescoRuntimeExceptionTest extends TestCase +{ + private static final String BASE_RESOURCE_NAME = "org.alfresco.i18n.testMessages"; + private static final String PARAM_VALUE = "television"; + private static final String MSG_PARAMS = "msg_params"; + private static final String MSG_ERROR = "msg_error"; + private static final String VALUE_ERROR = "This is an error message. \n This is on a new line."; + private static final String VALUE_FR_ERROR = "C'est un message d'erreur. \n C'est sur une nouvelle ligne."; + private static final String VALUE_PARAMS = "What no " + PARAM_VALUE + "?"; + private static final String VALUE_FR_PARAMS = "Que non " + PARAM_VALUE + "?"; + private static final String NON_I18NED_MSG = "This is a non i18ned error message."; + private static final String NON_EXISTING_MSG = "non.existing.msgId"; + + @Override + protected void setUp() throws Exception + { + // Re-set the current locale to be the default + Locale.setDefault(Locale.ENGLISH); + I18NUtil.setLocale(Locale.getDefault()); + } + + public void testI18NBehaviour() + { + // Ensure that the bundle is present on the classpath + String baseResourceAsProperty = BASE_RESOURCE_NAME.replace('.', '/') + ".properties"; + URL baseResourceURL = AlfrescoRuntimeExceptionTest.class.getClassLoader().getResource(baseResourceAsProperty); + assertNotNull(baseResourceURL); + + baseResourceAsProperty = BASE_RESOURCE_NAME.replace('.', '/') + "_fr_FR" + ".properties"; + baseResourceURL = AlfrescoRuntimeExceptionTest.class.getClassLoader().getResource(baseResourceAsProperty); + assertNotNull(baseResourceURL); + + // Ensure we can load it as a resource bundle + ResourceBundle properties = ResourceBundle.getBundle(BASE_RESOURCE_NAME); + assertNotNull(properties); + properties = ResourceBundle.getBundle(BASE_RESOURCE_NAME, new Locale("fr", "FR")); + assertNotNull(properties); + + + // From here on in, we use Spring + + // Register the bundle + I18NUtil.registerResourceBundle(BASE_RESOURCE_NAME); + + AlfrescoRuntimeException exception1 = new AlfrescoRuntimeException(MSG_PARAMS, new Object[]{PARAM_VALUE}); + assertTrue(exception1.getMessage().contains(VALUE_PARAMS)); + AlfrescoRuntimeException exception3 = new AlfrescoRuntimeException(MSG_ERROR); + assertTrue(exception3.getMessage().contains(VALUE_ERROR)); + + // Change the locale and re-test + I18NUtil.setLocale(new Locale("fr", "FR")); + + AlfrescoRuntimeException exception2 = new AlfrescoRuntimeException(MSG_PARAMS, new Object[]{PARAM_VALUE}); + assertTrue(exception2.getMessage().contains(VALUE_FR_PARAMS)); + AlfrescoRuntimeException exception4 = new AlfrescoRuntimeException(MSG_ERROR); + assertTrue(exception4.getMessage().contains(VALUE_FR_ERROR)); + + AlfrescoRuntimeException exception5 = new AlfrescoRuntimeException(NON_I18NED_MSG); + assertTrue(exception5.getMessage().contains(NON_I18NED_MSG)); + + // MNT-13028 + String param1 = PARAM_VALUE + "_1"; + String param2 = PARAM_VALUE + "_2"; + String param3 = PARAM_VALUE + "_3"; + AlfrescoRuntimeException exception6 = new AlfrescoRuntimeException(NON_EXISTING_MSG, new Object[]{param1, param2, param3}); + String message6 = exception6.getMessage(); + assertTrue(message6.contains(NON_EXISTING_MSG)); + assertTrue(message6.contains(param1)); + assertTrue(message6.contains(param2)); + assertTrue(message6.contains(param3)); + } + + public void testMakeRuntimeException() + { + Throwable e = new RuntimeException("sfsfs"); + RuntimeException ee = AlfrescoRuntimeException.makeRuntimeException(e, "Test"); + assertTrue("Exception should not have been changed", ee == e); + + e = new Exception(); + ee = AlfrescoRuntimeException.makeRuntimeException(e, "Test"); + assertTrue("Expected an AlfrescoRuntimeException instance", ee instanceof AlfrescoRuntimeException); + } +} diff --git a/src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java b/src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java new file mode 100644 index 0000000000..6f1721313c --- /dev/null +++ b/src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.ibatis; + +import java.util.AbstractCollection; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.TreeSet; + +import junit.framework.TestCase; + +import org.alfresco.util.resource.HierarchicalResourceLoader; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.context.support.ClassPathXmlApplicationContext; + + +/** + * @see HierarchicalSqlSessionFactoryBean + * @see HierarchicalXMLConfigBuilder + * @see HierarchicalResourceLoader + * + * @author Derek Hulley, janv + * @since 4.0 + */ +public class HierarchicalSqlSessionFactoryBeanTest extends TestCase +{ + private static final String QUERY_OBJECT = Object.class.getName(); + private static final String QUERY_ABSTRACTCOLLECTION = "org.alfresco.ibatis.abstractcollection."+AbstractCollection.class.getName().replace(".", "_"); + private static final String QUERY_ABSTRACTLIST = "org.alfresco.ibatis.abstractlist."+AbstractList.class.getName().replace(".", "_"); + private static final String QUERY_TREESET = "org.alfresco.ibatis.treeset."+TreeSet.class.getName().replace(".", "_"); + + private static Log logger = LogFactory.getLog(HierarchicalSqlSessionFactoryBeanTest.class); + + private ClassPathXmlApplicationContext ctx; + private TestDAO testDao; + + @Override + public void setUp() throws Exception + { + testDao = new TestDAO(); + testDao.setId(5L); + testDao.setPropOne("prop-one"); + testDao.setPropTwo("prop-two"); + } + + @Override + public void tearDown() throws Exception + { + try + { + if (ctx != null) + { + ctx.close(); + } + } + catch (Throwable e) + { + logger.error("Failed to neatly close application context", e); + } + } + + /** + * Pushes the dialect class into the system properties, closes an current context and + * recreates it; the MyBatis Configuration is then returned. + */ + @SuppressWarnings("unchecked") + private Configuration getConfiguration(Class dialectClass) throws Exception + { + System.setProperty("hierarchy-test.dialect", dialectClass.getName()); + if (ctx != null) + { + try + { + ctx.close(); + ctx = null; + } + catch (Throwable e) + { + logger.error("Failed to neatly close application context", e); + } + } + ctx = new ClassPathXmlApplicationContext("ibatis/hierarchy-test/hierarchy-test-context.xml"); + return ((SqlSessionFactory)ctx.getBean("mybatisConfig")).getConfiguration(); + } + + /** + * Check context startup and shutdown + */ + public void testContextStartup() throws Exception + { + getConfiguration(TreeSet.class); + getConfiguration(HashSet.class); + getConfiguration(ArrayList.class); + getConfiguration(AbstractCollection.class); + try + { + getConfiguration(Collection.class); + fail("Failed to detect incompatible class hierarchy"); + } + catch (Throwable e) + { + // Expected + } + } + + public void testHierarchyTreeSet() throws Exception + { + Configuration mybatisConfig = getConfiguration(TreeSet.class); + MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_TREESET); + assertNotNull("Query missing for " + QUERY_TREESET + " using " + TreeSet.class, stmt); + try + { + mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); + fail("Query not missing for " + QUERY_ABSTRACTCOLLECTION + " using " + TreeSet.class); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + public void testHierarchyHashSet() throws Exception + { + Configuration mybatisConfig = getConfiguration(HashSet.class); + MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); + assertNotNull("Query missing for " + QUERY_ABSTRACTCOLLECTION + " using " + HashSet.class, stmt); + try + { + mybatisConfig.getMappedStatement(QUERY_OBJECT); + fail("Query not missing for " + QUERY_OBJECT + " using " + HashSet.class); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + public void testHierarchyArrayList() throws Exception + { + Configuration mybatisConfig = getConfiguration(ArrayList.class); + MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_ABSTRACTLIST); + assertNotNull("Query missing for " + QUERY_ABSTRACTLIST + " using " + ArrayList.class, stmt); + try + { + mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); + fail("Query not missing for " + QUERY_ABSTRACTCOLLECTION + " using " + ArrayList.class); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + public void testHierarchyAbstractCollection() throws Exception + { + Configuration mybatisConfig = getConfiguration(AbstractCollection.class); + MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); + assertNotNull("Query missing for " + QUERY_ABSTRACTCOLLECTION + " using " + AbstractCollection.class, stmt); + try + { + mybatisConfig.getMappedStatement(QUERY_OBJECT); + fail("Query not missing for " + QUERY_OBJECT + " using " + AbstractCollection.class); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + /** + * Helper class that iBatis will use in the test mappings + * @author Derek Hulley + */ + public static class TestDAO + { + private Long id; + private String propOne; + private String propTwo; + + public Long getId() + { + return id; + } + public void setId(Long id) + { + this.id = id; + } + public String getPropOne() + { + return propOne; + } + public void setPropOne(String propOne) + { + this.propOne = propOne; + } + public String getPropTwo() + { + return propTwo; + } + public void setPropTwo(String propTwo) + { + this.propTwo = propTwo; + } + } +} diff --git a/src/test/java/org/alfresco/query/CannedQueryTest.java b/src/test/java/org/alfresco/query/CannedQueryTest.java new file mode 100644 index 0000000000..73c2b96a56 --- /dev/null +++ b/src/test/java/org/alfresco/query/CannedQueryTest.java @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.query; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import junit.framework.TestCase; + +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.util.Pair; +import org.alfresco.util.registry.NamedObjectRegistry; + +/** + * Tests the {@link CannedQuery name query} infrastructure. + * + * @author Derek Hulley + * @since 4.0 + */ +public class CannedQueryTest extends TestCase +{ + private static final String QUERY_TEST_ONE = "test.query.one"; + private static final String QUERY_TEST_TWO = "test.query.two"; + + private static final List RESULTS_ONE; + private static final List RESULTS_TWO; + private static final Set ANTI_RESULTS; + + static + { + RESULTS_ONE = new ArrayList(10); + for (int i = 0; i < 10; i++) + { + RESULTS_ONE.add("ONE_" + i); + } + RESULTS_TWO = new ArrayList(10); + for (int i = 0; i < 10; i++) + { + RESULTS_TWO.add(new Long(i)); + } + ANTI_RESULTS = new HashSet(); + ANTI_RESULTS.add("ONE_5"); + ANTI_RESULTS.add(new Long(5)); + } + + @SuppressWarnings("rawtypes") + private NamedObjectRegistry namedQueryFactoryRegistry; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public void setUp() throws Exception + { + // Create the registry + namedQueryFactoryRegistry = new NamedObjectRegistry(); + namedQueryFactoryRegistry.setStorageType(CannedQueryFactory.class); + namedQueryFactoryRegistry.setNamePattern("test\\.query\\..*"); + // Registry the query factories + // ONE + TestCannedQueryFactory namedQueryFactoryOne = new TestCannedQueryFactory(RESULTS_ONE); + namedQueryFactoryOne.setBeanName(QUERY_TEST_ONE); + namedQueryFactoryOne.setRegistry(namedQueryFactoryRegistry); + namedQueryFactoryOne.afterPropertiesSet(); + // TWO + TestCannedQueryFactory namedQueryFactoryTwo = new TestCannedQueryFactory(RESULTS_TWO); + namedQueryFactoryTwo.setBeanName(QUERY_TEST_TWO); + namedQueryFactoryTwo.setRegistry(namedQueryFactoryRegistry); + namedQueryFactoryTwo.afterPropertiesSet(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testRegistry() throws Exception + { + CannedQueryFactory one = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_ONE); + assertNotNull("No factory for " + QUERY_TEST_ONE, one); + CannedQueryFactory two = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_TWO); + assertNotNull("No factory for " + QUERY_TEST_TWO, two); + // Kick out registrations with incorrect naming convention + try + { + TestCannedQueryFactory namedQueryFactoryBogus = new TestCannedQueryFactory(RESULTS_TWO); + namedQueryFactoryBogus.setBeanName("test_query_blah"); + namedQueryFactoryBogus.setRegistry(namedQueryFactoryRegistry); + namedQueryFactoryBogus.afterPropertiesSet(); + fail("Should have kicked out incorrectly-named registered queries"); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + @SuppressWarnings("unchecked") + public void testQueryAllResults() throws Exception + { + // An instance of the CannedQueryFactory could be injected or constructed as well + CannedQueryFactory qfOne = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_ONE); + CannedQueryParameters params = new CannedQueryParameters(null); + CannedQuery qOne = qfOne.getCannedQuery(params); + CannedQueryResults qrOne = qOne.execute(); + // Attempt to reuse the query + try + { + qOne.execute(); + fail("Second execution of same instance must not be allowed."); + } + catch (IllegalStateException e) + { + // Expected + } + // Get the number of results when not requested + try + { + qrOne.getTotalResultCount(); + fail("Expected failure when requesting total count without explicit request."); + } + catch (IllegalStateException e) + { + // Expected + } + // Get the paged result count + int pagedResultCount = qrOne.getPagedResultCount(); + assertEquals("Incorrect number of results", 9, pagedResultCount); + assertEquals("No sorting was specified in the parameters", "ONE_0", qrOne.getPages().get(0).get(0)); + assertFalse("Should NOT have any more pages/items", qrOne.hasMoreItems()); + } + + @SuppressWarnings("unchecked") + public void testQueryMaxResults() throws Exception + { + // An instance of the CannedQueryFactory could be injected or constructed as well + CannedQueryFactory qfOne = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_ONE); + CannedQuery qOne = qfOne.getCannedQuery(null, 0, 9, null); + CannedQueryResults qrOne = qOne.execute(); + + // Get the paged result count + int pagedResultCount = qrOne.getPagedResultCount(); + assertEquals("Incorrect number of results", 9, pagedResultCount); + assertEquals("Incorrect number of pages", 1, qrOne.getPageCount()); + List> pages = qrOne.getPages(); + assertEquals("Incorrect number of pages", 1, pages.size()); + assertEquals("No sorting was specified in the parameters", "ONE_0", qrOne.getPages().get(0).get(0)); + assertEquals("No sorting was specified in the parameters", "ONE_9", qrOne.getPages().get(0).get(8)); + assertFalse("Should have more pages/items", qrOne.hasMoreItems()); + } + + @SuppressWarnings("unchecked") + public void testQueryPagedResults() throws Exception + { + CannedQueryFactory qfOne = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_ONE); + CannedQueryPageDetails qPageDetails = new CannedQueryPageDetails(0, 5, 1, 2); + CannedQueryParameters params = new CannedQueryParameters(null, qPageDetails, null); + CannedQuery qOne = qfOne.getCannedQuery(params); + CannedQueryResults qrOne = qOne.execute(); + // Check pages + assertEquals("Incorrect number of results", 9, qrOne.getPagedResultCount()); + assertEquals("No sorting was specified in the parameters", "ONE_0", qrOne.getPages().get(0).get(0)); + assertEquals("No sorting was specified in the parameters", "ONE_9", qrOne.getPages().get(1).get(3)); + List> pages = qrOne.getPages(); + assertEquals("Incorrect number of pages", 2, pages.size()); + assertEquals("Incorrect results on page", 5, pages.get(0).size()); + assertEquals("Incorrect results on page", 4, pages.get(1).size()); + assertFalse("Should NOT have any more pages/items", qrOne.hasMoreItems()); + + // Skip some results and use different page sizes + qPageDetails = new CannedQueryPageDetails(2, 3, 1, 3); + params = new CannedQueryParameters(null, qPageDetails, null); + qOne = qfOne.getCannedQuery(params); + qrOne = qOne.execute(); + // Check pages + assertEquals("Incorrect number of results", 7, qrOne.getPagedResultCount()); + assertEquals("Incorrect number of pages", 3, qrOne.getPageCount()); + pages = qrOne.getPages(); + assertEquals("Incorrect number of pages", 3, pages.size()); + assertEquals("Incorrect results on page", 3, pages.get(0).size()); + assertEquals("Incorrect results on page", 3, pages.get(1).size()); + assertEquals("Incorrect results on page", 1, pages.get(2).size()); + assertFalse("Should NOT have any more pages/items", qrOne.hasMoreItems()); + + // Skip some results and return less pages + qPageDetails = new CannedQueryPageDetails(2, 3, 1, 2); + params = new CannedQueryParameters(null, qPageDetails, null); + qOne = qfOne.getCannedQuery(params); + qrOne = qOne.execute(); + // Check pages + assertEquals("Incorrect number of results", 6, qrOne.getPagedResultCount()); + assertEquals("Incorrect number of pages", 2, qrOne.getPageCount()); + pages = qrOne.getPages(); + assertEquals("Incorrect number of pages", 2, pages.size()); + assertEquals("Incorrect results on page", 3, pages.get(0).size()); + assertEquals("Incorrect results on page", 3, pages.get(1).size()); + assertTrue("Should have more pages/items", qrOne.hasMoreItems()); + } + + @SuppressWarnings("unchecked") + public void testQuerySortedResults() throws Exception + { + CannedQueryFactory qfOne = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_ONE); + CannedQuerySortDetails qSortDetails = new CannedQuerySortDetails( + new Pair("blah", SortOrder.DESCENDING)); + CannedQueryParameters params = new CannedQueryParameters(null, null, qSortDetails); + CannedQuery qOne = qfOne.getCannedQuery(params); + CannedQueryResults qrOne = qOne.execute(); + // Check pages + assertEquals("Incorrect number of results", 9, qrOne.getPagedResultCount()); + assertEquals("Expected inverse sorting", "ONE_9", qrOne.getPages().get(0).get(0)); + assertEquals("Expected inverse sorting", "ONE_0", qrOne.getPages().get(0).get(8)); + assertFalse("Should NOT have any more pages/items", qrOne.hasMoreItems()); + } + + @SuppressWarnings("unchecked") + public void testQueryPermissionCheckedResults() throws Exception + { + CannedQueryFactory qfOne = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_ONE); + CannedQueryParameters params = new CannedQueryParameters(null, null, null, 0, null); + CannedQuery qOne = qfOne.getCannedQuery(params); + CannedQueryResults qrOne = qOne.execute(); + // Check pages + assertEquals("Incorrect number of results", 9, qrOne.getPagedResultCount()); + assertEquals("Incorrect result order", "ONE_0", qrOne.getPages().get(0).get(0)); + assertEquals("Incorrect result order", "ONE_1", qrOne.getPages().get(0).get(1)); + assertEquals("Incorrect result order", "ONE_2", qrOne.getPages().get(0).get(2)); + assertEquals("Incorrect result order", "ONE_3", qrOne.getPages().get(0).get(3)); + assertEquals("Incorrect result order", "ONE_4", qrOne.getPages().get(0).get(4)); // << missing 5! + assertEquals("Incorrect result order", "ONE_6", qrOne.getPages().get(0).get(5)); + assertEquals("Incorrect result order", "ONE_7", qrOne.getPages().get(0).get(6)); + assertEquals("Incorrect result order", "ONE_8", qrOne.getPages().get(0).get(7)); + assertEquals("Incorrect result order", "ONE_9", qrOne.getPages().get(0).get(8)); + assertFalse("Should NOT have any more pages/items", qrOne.hasMoreItems()); + } + + @SuppressWarnings("unchecked") + public void testQueryPermissionCheckedPagedTotalCount() throws Exception + { + CannedQueryFactory qfOne = namedQueryFactoryRegistry.getNamedObject(QUERY_TEST_ONE); + CannedQueryPageDetails qPageDetails = new CannedQueryPageDetails(5, 1, 1, 1); + CannedQuerySortDetails qSortDetails = new CannedQuerySortDetails( + new Pair("blah", SortOrder.DESCENDING)); + CannedQueryParameters params = new CannedQueryParameters(null, qPageDetails, qSortDetails, 1000, null); + CannedQuery qOne = qfOne.getCannedQuery(params); + CannedQueryResults qrOne = qOne.execute(); + // Check pages + assertEquals("Incorrect number of total results", + new Pair(9,9), qrOne.getTotalResultCount()); // Pre-paging + assertEquals("Incorrect number of paged results", 1, qrOne.getPagedResultCount()); // Skipped 5 + assertEquals("Incorrect result order", "ONE_3", qrOne.getPages().get(0).get(0)); // Order reversed + assertTrue("Should have more pages/items", qrOne.hasMoreItems()); + } + + /** + * Test factory to generate "queries" that just return a list of Strings. + * + * @param the type of the results + * + * @author Derek Hulley + * @since 4.0 + */ + private static class TestCannedQueryFactory extends AbstractCannedQueryFactory + { + private final List results; + private TestCannedQueryFactory(List results) + { + this.results = results; + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + String queryExecutionId = super.getQueryExecutionId(parameters); + return new TestCannedQuery(parameters, queryExecutionId, results, ANTI_RESULTS); + } + } + + /** + * Test query that just returns values passed in + * + * @param the type of the results + * + * @author Derek Hulley + * @since 4.0 + */ + private static class TestCannedQuery extends AbstractCannedQuery + { + private final List results; + private final Set antiResults; + private TestCannedQuery( + CannedQueryParameters params, + String queryExecutionId, List results, Set antiResults) + { + super(params); + this.results = results; + this.antiResults = antiResults; + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + return results; + } + + @Override + protected boolean isApplyPostQuerySorting() + { + return true; + } + + @Override + protected List applyPostQuerySorting(List results, CannedQuerySortDetails sortDetails) + { + if (sortDetails.getSortPairs().size() == 0) + { + // Nothing to sort on + return results; + } + List ret = new ArrayList(results); + Collections.reverse(ret); + return ret; + } + + @Override + protected boolean isApplyPostQueryPermissions() + { + return true; + } + + @Override + protected List applyPostQueryPermissions(List results, int requestedCount) + { + boolean cutoffAllowed = (getParameters().getTotalResultCountMax() == 0); + + final List ret = new ArrayList(results.size()); + for (T t : results) + { + if (!antiResults.contains(t)) + { + ret.add(t); + } + // Cut off if we have enough results + if (cutoffAllowed && ret.size() == requestedCount) + { + break; + } + } + + return ret; + } + } +} diff --git a/src/test/java/org/alfresco/util/BaseTest.java b/src/test/java/org/alfresco/util/BaseTest.java new file mode 100644 index 0000000000..6caebc4856 --- /dev/null +++ b/src/test/java/org/alfresco/util/BaseTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.springframework.extensions.config.ConfigSource; +import org.springframework.extensions.config.source.ClassPathConfigSource; +import org.springframework.extensions.config.source.FileConfigSource; +import org.springframework.extensions.config.xml.XMLConfigService; + +/** + * Base class for all JUnit tests + * + * @author gavinc, Neil McErlean + */ +public abstract class BaseTest extends TestCase +{ + protected String resourcesDir; + + protected boolean loadFromClasspath; + + public BaseTest() + { + // GC: Added this to allow flexible test resources folder configuration + // Try to get resources dir from a system property otherwise uses the default hardcoded + // backward compatible + String resourcesDir = null; + // This allows subclasses to override the getResourcesDir method (e.g. by passing classpath: ) + if(getResourcesDir()==null) + { + resourcesDir = System.getProperty("alfresco.test.resources.dir"); + if(resourcesDir == null || resourcesDir.equals("")) + { + // defaults to source/test-resources + resourcesDir = System.getProperty("user.dir") + File.separator + "source" + File.separator + "test-resources"; + } + } + else + { + resourcesDir = getResourcesDir(); + } + loadFromClasspath = resourcesDir.startsWith("classpath:"); + // Returns the resources dir with trailing separator or just the classpath: string in case that was specified + this.resourcesDir = resourcesDir + ((loadFromClasspath) ? "" : File.separator); + } + + /** + * Override this method to pass a custom resource dir. + * Valid values are a file system path or the string "classpath:" + * @return + */ + public String getResourcesDir() + { + return this.resourcesDir; + } + + /** + * Checks for validity of the resource location. + * + * In case of file resource, provide the full file path + * In case of classpath resources, please pass the full resource URI, prepended with the classpath: string + * @param fullFileName + */ + protected void assertFileIsValid(String fullFileName) + { + if(loadFromClasspath) + { + // if we load from classpath, we need to remove the "classpath:" trailing string + String resourceName = fullFileName.substring(fullFileName.indexOf(":") + 1); + assertNotNull(resourceName); + URL configResourceUrl = getClass().getClassLoader().getResource(resourceName); + assertNotNull(configResourceUrl); + } + else + { + File f = new File(fullFileName); + assertTrue("Required file missing: " + fullFileName, f.exists()); + assertTrue("Required file not readable: " + fullFileName, f.canRead()); + } + } + + /** + * Loads a config file from filesystem or classpath + * + * In case of file resource, just provide the file name relative to resourceDir + * In case of classpath resources, just provide the resource URI, without with the prepending classpath: string + * @param xmlConfigFile + * @return + */ + protected XMLConfigService initXMLConfigService(String xmlConfigFile) + { + XMLConfigService svc = null; + if(loadFromClasspath) + { + svc = new XMLConfigService(new ClassPathConfigSource(xmlConfigFile)); + } + else + { + String fullFileName = getResourcesDir() + xmlConfigFile; + assertFileIsValid(fullFileName); + svc = new XMLConfigService(new FileConfigSource(fullFileName)); + } + svc.initConfig(); + return svc; + } + + protected XMLConfigService initXMLConfigService(String xmlConfigFile, String overridingXmlConfigFile) + { + List files = new ArrayList(2); + files.add(xmlConfigFile); + files.add(overridingXmlConfigFile); + return initXMLConfigService(files); + } + + protected XMLConfigService initXMLConfigService(List xmlConfigFilenames) + { + List configFiles = new ArrayList(); + for (String filename : xmlConfigFilenames) + { + // if we load from classpath then no need to prepend the resources dir (which will be .equals("classpath:") + String path = ((loadFromClasspath) ? "" : getResourcesDir()) + filename; + assertFileIsValid(path); + configFiles.add(path); + } + ConfigSource configSource = (loadFromClasspath) ? new ClassPathConfigSource(configFiles) : new FileConfigSource(configFiles); + XMLConfigService svc = new XMLConfigService(configSource); + svc.initConfig(); + return svc; + } +} diff --git a/src/test/java/org/alfresco/util/BridgeTableTest.java b/src/test/java/org/alfresco/util/BridgeTableTest.java new file mode 100644 index 0000000000..2ea97a5bb0 --- /dev/null +++ b/src/test/java/org/alfresco/util/BridgeTableTest.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; + +import org.junit.Test; + +/** + * @author Andy + * + */ +public class BridgeTableTest extends TestCase +{ + + @Test + public void testBasic() + { + BridgeTable bridgeTable = new BridgeTable(); + bridgeTable.addLink("A", "B"); + bridgeTable.addLink("C", "D"); + bridgeTable.addLink("E", "F"); + + assertEquals(0, bridgeTable.getAncestors("A").size()); + assertEquals(1, bridgeTable.getAncestors("B").size()); + assertEquals(0, bridgeTable.getAncestors("C").size()); + assertEquals(1, bridgeTable.getAncestors("D").size()); + assertEquals(0, bridgeTable.getAncestors("E").size()); + assertEquals(1, bridgeTable.getAncestors("F").size()); + + assertEquals(1, bridgeTable.getDescendants("A").size()); + assertEquals(0, bridgeTable.getDescendants("B").size()); + assertEquals(1, bridgeTable.getDescendants("C").size()); + assertEquals(0, bridgeTable.getDescendants("D").size()); + assertEquals(1, bridgeTable.getDescendants("E").size()); + assertEquals(0, bridgeTable.getDescendants("F").size()); + + bridgeTable.addLink("B", "C"); + + + assertEquals(0, bridgeTable.getAncestors("A").size()); + assertEquals(1, bridgeTable.getAncestors("B").size()); + assertEquals(2, bridgeTable.getAncestors("C").size()); + assertEquals(3, bridgeTable.getAncestors("D").size()); + assertEquals(0, bridgeTable.getAncestors("E").size()); + assertEquals(1, bridgeTable.getAncestors("F").size()); + + assertEquals(3, bridgeTable.getDescendants("A").size()); + assertEquals(2, bridgeTable.getDescendants("B").size()); + assertEquals(1, bridgeTable.getDescendants("C").size()); + assertEquals(0, bridgeTable.getDescendants("D").size()); + assertEquals(1, bridgeTable.getDescendants("E").size()); + assertEquals(0, bridgeTable.getDescendants("F").size()); + + bridgeTable.addLink("D", "E"); + + assertEquals(0, bridgeTable.getAncestors("A").size()); + assertEquals(1, bridgeTable.getAncestors("B").size()); + assertTrue(bridgeTable.getAncestors("B", 1).contains("A")); + assertEquals(2, bridgeTable.getAncestors("C").size()); + assertTrue(bridgeTable.getAncestors("C", 1).contains("B")); + assertTrue(bridgeTable.getAncestors("C", 2).contains("A")); + assertEquals(3, bridgeTable.getAncestors("D").size()); + assertTrue(bridgeTable.getAncestors("D", 1).contains("C")); + assertTrue(bridgeTable.getAncestors("D", 2).contains("B")); + assertTrue(bridgeTable.getAncestors("D", 3).contains("A")); + assertEquals(4, bridgeTable.getAncestors("E").size()); + assertTrue(bridgeTable.getAncestors("E", 1).contains("D")); + assertTrue(bridgeTable.getAncestors("E", 2).contains("C")); + assertTrue(bridgeTable.getAncestors("E", 3).contains("B")); + assertTrue(bridgeTable.getAncestors("E", 4).contains("A")); + assertEquals(5, bridgeTable.getAncestors("F").size()); + assertTrue(bridgeTable.getAncestors("F", 1).contains("E")); + assertTrue(bridgeTable.getAncestors("F", 2).contains("D")); + assertTrue(bridgeTable.getAncestors("F", 3).contains("C")); + assertTrue(bridgeTable.getAncestors("F", 4).contains("B")); + assertTrue(bridgeTable.getAncestors("F", 5).contains("A")); + + assertEquals(5, bridgeTable.getDescendants("A").size()); + assertTrue(bridgeTable.getDescendants("A", 1).contains("B")); + assertTrue(bridgeTable.getDescendants("A", 2).contains("C")); + assertTrue(bridgeTable.getDescendants("A", 3).contains("D")); + assertTrue(bridgeTable.getDescendants("A", 4).contains("E")); + assertTrue(bridgeTable.getDescendants("A", 5).contains("F")); + assertEquals(4, bridgeTable.getDescendants("B").size()); + assertTrue(bridgeTable.getDescendants("B", 1).contains("C")); + assertTrue(bridgeTable.getDescendants("B", 2).contains("D")); + assertTrue(bridgeTable.getDescendants("B", 3).contains("E")); + assertTrue(bridgeTable.getDescendants("B", 4).contains("F")); + assertEquals(3, bridgeTable.getDescendants("C").size()); + assertTrue(bridgeTable.getDescendants("C", 1).contains("D")); + assertTrue(bridgeTable.getDescendants("C", 2).contains("E")); + assertTrue(bridgeTable.getDescendants("C", 3).contains("F")); + assertEquals(2, bridgeTable.getDescendants("D").size()); + assertTrue(bridgeTable.getDescendants("D", 1).contains("E")); + assertTrue(bridgeTable.getDescendants("D", 2).contains("F")); + assertEquals(1, bridgeTable.getDescendants("E").size()); + assertTrue(bridgeTable.getDescendants("E", 1).contains("F")); + assertEquals(0, bridgeTable.getDescendants("F").size()); + + bridgeTable.removeLink("D", "E"); + + assertEquals(0, bridgeTable.getAncestors("A").size()); + assertEquals(1, bridgeTable.getAncestors("B").size()); + assertEquals(2, bridgeTable.getAncestors("C").size()); + assertEquals(3, bridgeTable.getAncestors("D").size()); + assertEquals(0, bridgeTable.getAncestors("E").size()); + assertEquals(1, bridgeTable.getAncestors("F").size()); + + assertEquals(3, bridgeTable.getDescendants("A").size()); + assertEquals(2, bridgeTable.getDescendants("B").size()); + assertEquals(1, bridgeTable.getDescendants("C").size()); + assertEquals(0, bridgeTable.getDescendants("D").size()); + assertEquals(1, bridgeTable.getDescendants("E").size()); + assertEquals(0, bridgeTable.getDescendants("F").size()); + + bridgeTable.removeLink("B", "C"); + + assertEquals(0, bridgeTable.getAncestors("A").size()); + assertEquals(1, bridgeTable.getAncestors("B").size()); + assertEquals(0, bridgeTable.getAncestors("C").size()); + assertEquals(1, bridgeTable.getAncestors("D").size()); + assertEquals(0, bridgeTable.getAncestors("E").size()); + assertEquals(1, bridgeTable.getAncestors("F").size()); + + assertEquals(1, bridgeTable.getDescendants("A").size()); + assertEquals(0, bridgeTable.getDescendants("B").size()); + assertEquals(1, bridgeTable.getDescendants("C").size()); + assertEquals(0, bridgeTable.getDescendants("D").size()); + assertEquals(1, bridgeTable.getDescendants("E").size()); + assertEquals(0, bridgeTable.getDescendants("F").size()); + } + +// @Test +// public void test_1M() +// { +// // 1M = 21 +// for (int i = 0; i < 20; i++) +// { +// BridgeTable bridgeTable = new BridgeTable(); +// long start = System.nanoTime(); +// bridgeTable.addLinks(getTreeLinks(i)); +// long end = System.nanoTime(); +// double elapsed = ((end - start) / 1e9); +// System.out.println("" + bridgeTable.size() + " in " + elapsed); +// assertTrue(elapsed < 60); +// } +// } + + @Test + public void test_16k() + { + // 1M = 21 + for (int i = 0; i < 15; i++) + { + BridgeTable bridgeTable = new BridgeTable(); + long start = System.nanoTime(); + bridgeTable.addLinks(getTreeLinks(i)); + long end = System.nanoTime(); + double elapsed = ((end - start) / 1e9); + System.out.println("" + bridgeTable.size() + " in " + elapsed); + assertTrue(elapsed < 60); + } + } + +// @Test +// public void test_1000x1000() +// { +// BridgeTable bridgeTable = new BridgeTable(); +// HashSet> links = new HashSet>(); +// for (int i = 0; i < 10; i++) +// { +// for (int j = 0; j < 100; j++) +// { +// links.addAll(getTreeLinks(10)); +// } +// +// +// long start = System.nanoTime(); +// bridgeTable.addLinks(links); +// long end = System.nanoTime(); +// System.out.println("Trees " + bridgeTable.size() + " in " + ((end - start) / 1e9)); +// +// start = System.nanoTime(); +// for (String key : bridgeTable.keySet()) +// { +// bridgeTable.getAncestors(key); +// } +// end = System.nanoTime(); +// System.out.println("By key " + bridgeTable.size() + " in " + ((end - start) / 1e9)); +// } +// } + + + @Test + public void test_100x100() + { + BridgeTable bridgeTable = new BridgeTable(); + HashSet> links = new HashSet>(); + for (int i = 0; i < 10; i++) + { + for (int j = 0; j < 10; j++) + { + links.addAll(getTreeLinks(7)); + } + + + long start = System.nanoTime(); + bridgeTable.addLinks(links); + long end = System.nanoTime(); + System.out.println("Trees " + bridgeTable.size() + " in " + ((end - start) / 1e9)); + + start = System.nanoTime(); + for (String key : bridgeTable.keySet()) + { + bridgeTable.getAncestors(key); + } + end = System.nanoTime(); + System.out.println("By key " + bridgeTable.size() + " in " + ((end - start) / 1e9)); + } + } + + @Test + public void testSecondary() + { + BridgeTable bridgeTable = new BridgeTable(); + + bridgeTable.addLink("A", "B"); + bridgeTable.addLink("A", "C"); + bridgeTable.addLink("B", "D"); + bridgeTable.addLink("B", "E"); + bridgeTable.addLink("C", "F"); + bridgeTable.addLink("C", "G"); + + assertEquals(2, bridgeTable.getDescendants("A", 1).size()); + assertEquals(4, bridgeTable.getDescendants("A", 2).size()); + assertEquals(6, bridgeTable.getDescendants("A", 1, 2).size()); + assertEquals(2, bridgeTable.getDescendants("B", 1).size()); + assertEquals(0, bridgeTable.getDescendants("B", 2).size()); + assertEquals(2, bridgeTable.getDescendants("B", 1, 2).size()); + + bridgeTable.addLink("N", "O"); + bridgeTable.addLink("N", "P"); + bridgeTable.addLink("O", "Q"); + bridgeTable.addLink("O", "R"); + bridgeTable.addLink("P", "S"); + bridgeTable.addLink("P", "T"); + + assertEquals(2, bridgeTable.getDescendants("N", 1).size()); + assertEquals(4, bridgeTable.getDescendants("N", 2).size()); + assertEquals(6, bridgeTable.getDescendants("N", 1, 2).size()); + assertEquals(2, bridgeTable.getDescendants("O", 1).size()); + assertEquals(0, bridgeTable.getDescendants("O", 2).size()); + assertEquals(2, bridgeTable.getDescendants("O", 1, 2).size()); + + bridgeTable.addLink("A", "N"); + assertEquals(3, bridgeTable.getDescendants("A", 1).size()); + assertEquals(2, bridgeTable.getDescendants("B", 1).size()); + + bridgeTable.addLink("A", "N"); + assertEquals(3, bridgeTable.getDescendants("A", 1).size()); + assertEquals(2, bridgeTable.getDescendants("B", 1).size()); + + bridgeTable.addLink("B", "N"); + assertEquals(3, bridgeTable.getDescendants("A", 1).size()); + assertEquals(3, bridgeTable.getDescendants("B", 1).size()); + + bridgeTable.addLink("B", "N"); + assertEquals(3, bridgeTable.getDescendants("A", 1).size()); + assertEquals(3, bridgeTable.getDescendants("B", 1).size()); + + bridgeTable.removeLink("A", "N"); + assertEquals(3, bridgeTable.getDescendants("A", 1).size()); + assertEquals(3, bridgeTable.getDescendants("B", 1).size()); + + bridgeTable.removeLink("A", "N"); + assertEquals(2, bridgeTable.getDescendants("A", 1).size()); + assertEquals(3, bridgeTable.getDescendants("B", 1).size()); + + bridgeTable.removeLink("B", "N"); + assertEquals(2, bridgeTable.getDescendants("A", 1).size()); + assertEquals(3, bridgeTable.getDescendants("B", 1).size()); + + bridgeTable.removeLink("B", "N"); + + assertEquals(2, bridgeTable.getDescendants("A", 1).size()); + assertEquals(4, bridgeTable.getDescendants("A", 2).size()); + assertEquals(6, bridgeTable.getDescendants("A", 1, 2).size()); + assertEquals(2, bridgeTable.getDescendants("B", 1).size()); + assertEquals(0, bridgeTable.getDescendants("B", 2).size()); + assertEquals(2, bridgeTable.getDescendants("B", 1, 2).size()); + + + assertEquals(2, bridgeTable.getDescendants("N", 1).size()); + assertEquals(4, bridgeTable.getDescendants("N", 2).size()); + assertEquals(6, bridgeTable.getDescendants("N", 1, 2).size()); + assertEquals(2, bridgeTable.getDescendants("O", 1).size()); + assertEquals(0, bridgeTable.getDescendants("O", 2).size()); + assertEquals(2, bridgeTable.getDescendants("O", 1, 2).size()); + + } + + private Set> getTreeLinks(int depth) + { + int count = 0; + String base = "" + System.nanoTime(); + + HashSet currentRow = new HashSet(); + HashSet lastRow = new HashSet(); + + HashSet> links = new HashSet>(); + + for (int i = 0; i < depth; i++) + { + if (lastRow.size() == 0) + { + currentRow.add("GROUP_" + base + "_" + count); + count++; + } + else + { + for (String group : lastRow) + { + String newGroup = "GROUP_" + base + "_" + count; + currentRow.add(newGroup); + count++; + links.add(new Pair(group, newGroup)); + + newGroup = "GROUP_" + base + "_" + count; + currentRow.add(newGroup); + count++; + links.add(new Pair(group, newGroup)); + } + } + lastRow = currentRow; + currentRow = new HashSet(); + } + + return links; + + } + +} diff --git a/src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java b/src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java new file mode 100644 index 0000000000..c5ace65cdf --- /dev/null +++ b/src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import junit.framework.TestCase; + +/** + * Tests for our instance of {@link java.util.concurrent.ThreadPoolExecutor} + * + * @author Nick Burch + */ +public class DynamicallySizedThreadPoolExecutorTest extends TestCase +{ + + private static Log logger = LogFactory.getLog(DynamicallySizedThreadPoolExecutorTest.class); + private static final int DEFAULT_KEEP_ALIVE_TIME = 90; + + @Override + protected void setUp() throws Exception + { + SleepUntilAllWake.reset(); + } + + public void testUpToCore() throws Exception + { + DynamicallySizedThreadPoolExecutor exec = createInstance(5,10, DEFAULT_KEEP_ALIVE_TIME); + + assertEquals(0, exec.getPoolSize()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(2, exec.getPoolSize()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(4, exec.getPoolSize()); + exec.execute(new SleepUntilAllWake()); + assertEquals(5, exec.getPoolSize()); + + SleepUntilAllWake.wakeAll(); + Thread.sleep(100); + assertEquals(5, exec.getPoolSize()); + } + + public void testPastCoreButNotHugeQueue() throws Exception + { + DynamicallySizedThreadPoolExecutor exec = createInstance(5,10, DEFAULT_KEEP_ALIVE_TIME); + + assertEquals(0, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(5, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + + // Need to hit max pool size before it adds more + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(5, exec.getPoolSize()); + assertEquals(5, exec.getQueue().size()); + + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(5, exec.getPoolSize()); + assertEquals(7, exec.getQueue().size()); + + SleepUntilAllWake.wakeAll(); + Thread.sleep(100); + assertEquals(5, exec.getPoolSize()); + } + + public void testToExpandQueue() throws Exception + { + DynamicallySizedThreadPoolExecutor exec = createInstance(2,4,1); + + assertEquals(0, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(2, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(2, exec.getPoolSize()); + assertEquals(3, exec.getQueue().size()); + + // Next should add one + exec.execute(new SleepUntilAllWake()); + Thread.sleep(20); // Let the new thread spin up + assertEquals(3, exec.getPoolSize()); + assertEquals(3, exec.getQueue().size()); + + // And again + exec.execute(new SleepUntilAllWake()); + Thread.sleep(20); // Let the new thread spin up + assertEquals(4, exec.getPoolSize()); + assertEquals(3, exec.getQueue().size()); + + // But no more will be added, as we're at max + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(4, exec.getPoolSize()); + assertEquals(6, exec.getQueue().size()); + + SleepUntilAllWake.wakeAll(); + Thread.sleep(100); + + // All threads still running, as 1 second timeout + assertEquals(4, exec.getPoolSize()); + } + + public void offTestToExpandThenContract() throws Exception + { + DynamicallySizedThreadPoolExecutor exec = createInstance(2,4,1); + exec.setKeepAliveTime(30, TimeUnit.MILLISECONDS); + + assertEquals(0, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(2, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(2, exec.getPoolSize()); + assertEquals(3, exec.getQueue().size()); + + // Next should add one + exec.execute(new SleepUntilAllWake()); + Thread.sleep(20); // Let the new thread spin up + assertEquals(3, exec.getPoolSize()); + assertEquals(3, exec.getQueue().size()); + + // And again + exec.execute(new SleepUntilAllWake()); + Thread.sleep(20); // Let the new thread spin up + assertEquals(4, exec.getPoolSize()); + assertEquals(3, exec.getQueue().size()); + + // But no more will be added, as we're at max + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(4, exec.getPoolSize()); + assertEquals(6, exec.getQueue().size()); + + SleepUntilAllWake.wakeAll(); + Thread.sleep(100); + + // Wait longer than the timeout without any work, which should + // let all the extra threads go away + // (Depending on how closely your JVM follows the specification, + // we may fall back to the core size which is correct, or we + // may go to zero which is wrong, but hey, it's the JVM...) + logger.debug("Core pool size is " + exec.getCorePoolSize()); + logger.debug("Current pool size is " + exec.getPoolSize()); + logger.debug("Queue size is " + exec.getQueue().size()); + assertTrue( + "Pool size should be 0-2 as everything is idle, was " + exec.getPoolSize(), + exec.getPoolSize() >= 0 + ); + assertTrue( + "Pool size should be 0-2 as everything is idle, was " + exec.getPoolSize(), + exec.getPoolSize() <= 2 + ); + + SleepUntilAllWake.reset(); + + // Add 2 new jobs, will stay/ go to at 2 threads + assertEquals(0, exec.getQueue().size()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + + // Let the idle threads grab them, then check + Thread.sleep(20); + assertEquals(2, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + + // 3 more, still at 2 threads + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + assertEquals(2, exec.getPoolSize()); + assertEquals(3, exec.getQueue().size()); + + // And again wait for it all + SleepUntilAllWake.wakeAll(); + Thread.sleep(100); + assertEquals(2, exec.getPoolSize()); + + + // Now decrease the overall pool size + // Will rise and fall to there now + exec.setCorePoolSize(1); + + // Run a quick job, to ensure that the + // "can I kill one yet" logic is applied + SleepUntilAllWake.reset(); + exec.execute(new SleepUntilAllWake()); + SleepUntilAllWake.wakeAll(); + + Thread.sleep(100); + assertEquals(1, exec.getPoolSize()); + assertEquals(0, exec.getQueue().size()); + + SleepUntilAllWake.reset(); + + + // Push enough on to go up to 4 active threads + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + exec.execute(new SleepUntilAllWake()); + + Thread.sleep(20); // Let the new threads spin up + assertEquals(4, exec.getPoolSize()); + assertEquals(6, exec.getQueue().size()); + + // Wait for them all to finish, should drop back to 1 now + // (Or zero, if your JVM can't read the specification...) + SleepUntilAllWake.wakeAll(); + Thread.sleep(100); + assertTrue( + "Pool size should be 0 or 1 as everything is idle, was " + exec.getPoolSize(), + exec.getPoolSize() >= 0 + ); + assertTrue( + "Pool size should be 0 or 1 as everything is idle, was " + exec.getPoolSize(), + exec.getPoolSize() <= 1 + ); + } + + private DynamicallySizedThreadPoolExecutor createInstance(int corePoolSize, int maximumPoolSize, int keepAliveTime) + { + // We need a thread factory + TraceableThreadFactory threadFactory = new TraceableThreadFactory(); + threadFactory.setThreadDaemon(true); + threadFactory.setThreadPriority(Thread.NORM_PRIORITY); + + BlockingQueue workQueue = new LinkedBlockingQueue(); + + return new DynamicallySizedThreadPoolExecutor( + corePoolSize, + maximumPoolSize, + keepAliveTime, + TimeUnit.SECONDS, + workQueue, + threadFactory, + new ThreadPoolExecutor.CallerRunsPolicy()); + } + + public static class SleepUntilAllWake implements Runnable + { + private static ConcurrentMap sleeping = new ConcurrentHashMap(); + private static boolean allAwake = false; + + @Override + public void run() + { + if(allAwake) return; + + // Track us, and wait for the bang + logger.debug("Adding thread: " + Thread.currentThread().getName()); + sleeping.put(Thread.currentThread().getName(), Thread.currentThread()); + try + { + Thread.sleep(30*1000); + System.err.println("Warning - Thread finished sleeping without wake!"); + } + catch(InterruptedException e) + { + logger.debug("Interrupted thread: " + Thread.currentThread().getName()); + } + } + + public static void wakeAll() + { + allAwake = true; + for(Entry t : sleeping.entrySet()) + { + logger.debug("Interrupting thread: " + t.getKey()); + t.getValue().interrupt(); + } + } + public static void reset() + { + logger.debug("Resetting."); + allAwake = false; + sleeping.clear(); + } + } +} diff --git a/src/test/java/org/alfresco/util/EqualsHelperTest.java b/src/test/java/org/alfresco/util/EqualsHelperTest.java new file mode 100644 index 0000000000..c40d8afa34 --- /dev/null +++ b/src/test/java/org/alfresco/util/EqualsHelperTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.util.EqualsHelper.MapValueComparison; + +import junit.framework.TestCase; + +/** + * @see EqualsHelper + * + * @author Derek Hulley + * @since 3.1SP2 + */ +public class EqualsHelperTest extends TestCase +{ + private File fileOne; + private File fileTwo; + + @Override + public void setUp() throws Exception + { + fileOne = TempFileProvider.createTempFile(getName(), "-one.txt"); + fileTwo = TempFileProvider.createTempFile(getName(), "-two.txt"); + + OutputStream osOne = new FileOutputStream(fileOne); + osOne.write("1234567890 - ONE".getBytes("UTF-8")); + osOne.close(); + + OutputStream osTwo = new FileOutputStream(fileTwo); + osTwo.write("1234567890 - TWO".getBytes("UTF-8")); + osTwo.close(); + } + + public void testStreamsNotEqual() throws Exception + { + InputStream isLeft = new FileInputStream(fileOne); + InputStream isRight = new FileInputStream(fileTwo); + boolean equal = EqualsHelper.binaryStreamEquals(isLeft, isRight); + assertFalse("Should not be the same", equal); + } + + public void testStreamsEqual() throws Exception + { + InputStream isLeft = new FileInputStream(fileOne); + InputStream isRight = new FileInputStream(fileOne); + boolean equal = EqualsHelper.binaryStreamEquals(isLeft, isRight); + assertTrue("Should be the same", equal); + } + + public void testMapComparison() throws Exception + { + Map left = new HashMap(); + Map right = new HashMap(); + // EQUAL + left.put(0, "A"); + right.put(0, "A"); + // NOT_EQUAL + left.put(1, "A"); + right.put(1, "B"); + // EQUAL + left.put(2, null); + right.put(2, null); + // NOT_EQUAL + left.put(3, null); + right.put(3, "B"); + // NOT_EQUAL + left.put(4, "A"); + right.put(4, null); + // RIGHT_ONLY + right.put(5, "B"); + // LEFT_ONLY + left.put(6, "A"); + Map diff = EqualsHelper.getMapComparison(left, right); + assertEquals("'EQUAL' check failed", MapValueComparison.EQUAL, diff.get(0)); + assertEquals("'NOT_EQUAL' check failed", MapValueComparison.NOT_EQUAL, diff.get(1)); + assertEquals("'EQUAL' check failed", MapValueComparison.EQUAL, diff.get(2)); + assertEquals("'NOT_EQUAL' check failed", MapValueComparison.NOT_EQUAL, diff.get(3)); + assertEquals("'NOT_EQUAL' check failed", MapValueComparison.NOT_EQUAL, diff.get(4)); + assertEquals("'RIGHT_ONLY' check failed", MapValueComparison.RIGHT_ONLY, diff.get(5)); + assertEquals("'LEFT_ONLY' check failed", MapValueComparison.LEFT_ONLY, diff.get(6)); + } +} diff --git a/src/test/java/org/alfresco/util/GuidTest.java b/src/test/java/org/alfresco/util/GuidTest.java new file mode 100644 index 0000000000..a99f96b00d --- /dev/null +++ b/src/test/java/org/alfresco/util/GuidTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2016 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.util; + +import java.lang.Thread.State; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import junit.framework.TestCase; + +import org.junit.Assert; + +/** + * Test class for GUID generation + * + * @author Andreea Dragoi + * + */ +public class GuidTest extends TestCase +{ + class GuidRunner implements Runnable + { + @Override + public void run() + { + GUID.generate(); + } + } + + /** + * Tests the improvement added by using a SecureRandom pool when generating GUID's + */ + public void testGuid() + { + // warm-up (to pre-init the secureRandomArray) + GUID.generate(); + + List threads = new ArrayList<>(); + int n = 30; + + for (int i = 0; i < n; i++) + { + Thread thread = new Thread(new GuidRunner()); + threads.add(thread); + thread.start(); + } + + Set blocked = new HashSet(); + Set terminated = new HashSet(); + + int maxItemsBlocked = 0; + + while (terminated.size() != n) + { + for (Thread current : threads) + { + State state = current.getState(); + String name = current.getName(); + + if (state == State.BLOCKED) + { + if (!blocked.contains(name)) + { + blocked.add(name); + maxItemsBlocked = blocked.size() > maxItemsBlocked ? blocked.size() : maxItemsBlocked; + } + } + else // not BLOCKED, eg. RUNNABLE, TERMINATED, ... + { + blocked.remove(name); + if (state == State.TERMINATED && !terminated.contains(name)) + { + terminated.add(name); + } + } + } + } + + //worst case scenario : max number of threads blocked at a moment = number of threads - 2 ( usually ~5 for 30 threads) + //the implementation without RandomSecure pool reaches constantly (number of threads - 1) max blocked threads + Assert.assertTrue("Exceeded number of blocked threads : " + maxItemsBlocked, maxItemsBlocked < n-2); + } + +} + diff --git a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java new file mode 100644 index 0000000000..96c23f4314 --- /dev/null +++ b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2005-2016 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import junit.framework.TestCase; + +import org.alfresco.error.AlfrescoRuntimeException; + +public class ISO8601DateFormatTest extends TestCase +{ + public void testConversion() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + String test = "2005-09-16T17:01:03.456+01:00"; + String test2 = "1801-09-16T17:01:03.456+01:00"; + // convert to a date + Date date = ISO8601DateFormat.parse(test); + Date date2 = ISO8601DateFormat.parse(test2); + // get the string form + String strDate = ISO8601DateFormat.format(date); + String strDate2 = ISO8601DateFormat.format(date2); + // convert back to a date from the converted string + Date dateAfter = ISO8601DateFormat.parse(strDate); + Date dateAfter2 = ISO8601DateFormat.parse(strDate2); + // make sure the date objects match, test this instead of the + // string as the string form will be different in different + // locales + assertEquals(date, dateAfter); + assertEquals(date2, dateAfter2); + } + + public void testGetCalendarMethod() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + Calendar calendarGMT = ISO8601DateFormat.getCalendar(); + + TimeZone.setDefault(TimeZone.getTimeZone("BST")); + Calendar calendarBST = ISO8601DateFormat.getCalendar(); + + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + Calendar calendarGMT1 = ISO8601DateFormat.getCalendar(); + + assertNotSame(calendarGMT, calendarBST); + assertSame(calendarGMT, calendarGMT1); + } + + public void testDateParser() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + String test = "2005-09-16T17:01:03.456+01:00"; + String test2 = "1801-09-16T17:01:03.456+01:00"; + + String isoFormattedDate = "2005-09-16T16:01:03.456Z"; + String isoFormattedDate2 = "1801-09-16T16:01:03.456Z"; + + Date testDate = getDateValue(2005, 9, 16, 17, 1, 3, 456, 60); + Date testDate2 = getDateValue(1801, 9, 16, 17, 1, 3, 456, 60); + + // convert to a date + Date date = ISO8601DateFormat.parse(test); + Date date2 = ISO8601DateFormat.parse(test2); + // check converted to date value + assertEquals(testDate, date); + assertEquals(testDate2, date2); + + // get the string form + String strDate = ISO8601DateFormat.format(date); + String strDate2 = ISO8601DateFormat.format(date2); + // check the date converted to sting + assertEquals(isoFormattedDate, strDate); + assertEquals(isoFormattedDate2, strDate2); + } + + private Date getDateValue(int year, int month, int day, int hours, int minutes, int sec, int msec, int offsetInMinutes) + { + // minute in millis + int millisInMinute = 1000 * 60; + + GregorianCalendar gc = new GregorianCalendar(); + + // set correct offset + String[] tzArray = TimeZone.getAvailableIDs(millisInMinute * offsetInMinutes); + if (tzArray.length > 0) + { + gc.setTimeZone(TimeZone.getTimeZone(tzArray[0])); + } + + // set date + gc.set(GregorianCalendar.YEAR, year); + gc.set(GregorianCalendar.MONTH, month - 1); + gc.set(GregorianCalendar.DAY_OF_MONTH, day); + gc.set(GregorianCalendar.HOUR_OF_DAY, hours); + gc.set(GregorianCalendar.MINUTE, minutes); + gc.set(GregorianCalendar.SECOND, sec); + gc.set(GregorianCalendar.MILLISECOND, msec); + + return gc.getTime(); + } + + public void testMiliseconds() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + // ALF-3803 bug fix, milliseconds are optional + String testA = "2005-09-16T17:01:03.456Z"; + String testB = "2005-09-16T17:01:03Z"; + String testBms = "2005-09-16T17:01:03.000Z"; + String testC = "1801-09-16T17:01:03Z"; + String testCms = "1801-09-16T17:01:03.000Z"; + + Date dateA = ISO8601DateFormat.parse(testA); + Date dateB = ISO8601DateFormat.parse(testB); + Date dateC = ISO8601DateFormat.parse(testC); + + assertEquals(testA, ISO8601DateFormat.format(dateA)); + + assertEquals(testBms, ISO8601DateFormat.format(dateB)); + + assertEquals(testCms, ISO8601DateFormat.format(dateC)); + + // The official ISO 8601.2004 spec doesn't say much helpful about milliseconds + // The W3C version says it's up to different + // implementations to put bounds on them + // We can silently ignore anything beyond 3 digits, see ALF-14687 + String testCms3 = "2005-09-16T17:01:03.123+01:00"; + String testCms4 = "2005-09-16T17:01:03.1234+01:00"; + String testCms5 = "2005-09-16T17:01:03.12345+01:00"; + String testCms6 = "2005-09-16T17:01:03.123456+01:00"; + String testCms7 = "2005-09-16T17:01:03.1234567+01:00"; + + Date testCDate = ISO8601DateFormat.parse(testCms3); + assertEquals(testCDate, ISO8601DateFormat.parse(testCms4)); + assertEquals(testCDate, ISO8601DateFormat.parse(testCms5)); + assertEquals(testCDate, ISO8601DateFormat.parse(testCms6)); + assertEquals(testCDate, ISO8601DateFormat.parse(testCms7)); + } + + public void testTimezones() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + Date date = null; + + TimeZone tz = TimeZone.getTimeZone("Australia/Sydney"); + String testSydney = "2011-02-04T16:13:14"; + String testUTC = "2011-02-04T05:13:14.000Z"; + + //Sydney + date = ISO8601DateFormat.parse(testSydney, tz); + assertEquals(testUTC, ISO8601DateFormat.format(date)); + + // Check with ms too + date = ISO8601DateFormat.parse(testSydney + ".000", tz); + assertEquals(testUTC, ISO8601DateFormat.format(date)); + + //Sydney with an offset and timezone + date = ISO8601DateFormat.parse(testSydney+"+11:00", tz); + assertEquals(testUTC, ISO8601DateFormat.format(date)); + + // Check with ms too + date = ISO8601DateFormat.parse(testSydney + ".000"+"+11:00", tz); + assertEquals(testUTC, ISO8601DateFormat.format(date)); + } + + public void testToZulu(){ + String base = "2011-02-04T16:13:14.000"; + String zulu = base + "Z"; + String utc0 = base + "+00:00"; + String utc1 = "2011-02-04T17:13:14" + "+01:00"; + String utcMinus1 = "2011-02-04T15:13:14" + "-01:00"; + + assertEquals(zulu, ISO8601DateFormat.formatToZulu(zulu)); + assertEquals(zulu, ISO8601DateFormat.formatToZulu(utc1)); + assertEquals(zulu, ISO8601DateFormat.formatToZulu(utc0)); + assertEquals(zulu, ISO8601DateFormat.formatToZulu(utcMinus1)); + } + + public void testDayOnly() + { + Date date = null; + + // Test simple parsing + TimeZone tz = TimeZone.getTimeZone("Europe/London"); + date = ISO8601DateFormat.parseDayOnly("2012-05-21", tz); + + Calendar cal = Calendar.getInstance(tz); + cal.setTime(date); + + // Check date and time component + assertEquals(2012, cal.get(Calendar.YEAR)); + assertEquals(4, cal.get(Calendar.MONTH)); + assertEquals(21, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(0, cal.get(Calendar.HOUR)); + assertEquals(0, cal.get(Calendar.MINUTE)); + assertEquals(0, cal.get(Calendar.SECOND)); + assertEquals(0, cal.get(Calendar.MILLISECOND)); + + // Check time is ignored on full ISO8601-string + date = ISO8601DateFormat.parseDayOnly("2012-05-21T12:13:14Z", tz); + cal = Calendar.getInstance(tz); + cal.setTime(date); + + assertEquals(2012, cal.get(Calendar.YEAR)); + assertEquals(4, cal.get(Calendar.MONTH)); + assertEquals(21, cal.get(Calendar.DAY_OF_MONTH)); + assertEquals(0, cal.get(Calendar.HOUR)); + assertEquals(0, cal.get(Calendar.MINUTE)); + assertEquals(0, cal.get(Calendar.SECOND)); + assertEquals(0, cal.get(Calendar.MILLISECOND)); + + // Check year signs + date = ISO8601DateFormat.parseDayOnly("+2012-05-21", tz); + cal = Calendar.getInstance(tz); + cal.setTime(date); + assertEquals(GregorianCalendar.AD, cal.get(Calendar.ERA)); + + date = ISO8601DateFormat.parseDayOnly("-2012-05-21", tz); + cal = Calendar.getInstance(tz); + cal.setTime(date); + assertEquals(GregorianCalendar.BC, cal.get(Calendar.ERA)); + + + // Check illegal format + try + { + ISO8601DateFormat.parseDayOnly("2011-02-0", tz); + fail("Exception expected on illegal format"); + } + catch(AlfrescoRuntimeException e) {} + try + { + ISO8601DateFormat.parseDayOnly("201a-02-02", tz); + fail("Exception expected on illegal format"); + } + catch(AlfrescoRuntimeException e) {} + } + + public void testDSTParser() + { + TimeZone tz = TimeZone.getTimeZone("America/Sao_Paulo"); + TimeZone.setDefault(tz); + // MNT-15454: This date is invalid as the 00:00 hour became 01:00 because of daylight saving time. + String test1 = "2014-10-19T"; + String test2 = "2014-10-19T00:01:01.000"; + + String isoFormattedDate = "2014-10-19T03:00:00.000Z"; + + // Sun Oct 19 01:00:00 BRST 2014 + Date testDate = getDateValue(2014, 10, 19, 0, 0, 0, 0, - 3*60); + // convert to a date + Date date = ISO8601DateFormat.parse(test1, tz); + // Check converted to date value + assertEquals(testDate, date); + + // Convert to a date + date = ISO8601DateFormat.parse(test2, tz); + // Check converted to date value + assertEquals(testDate, date); + + // Get the string form + String strDate = ISO8601DateFormat.format(date); + // Check the date converted to sting + assertEquals(isoFormattedDate, strDate); + } +} diff --git a/src/test/java/org/alfresco/util/LogAdapterTest.java b/src/test/java/org/alfresco/util/LogAdapterTest.java new file mode 100644 index 0000000000..6d707e77af --- /dev/null +++ b/src/test/java/org/alfresco/util/LogAdapterTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test class for LogAdapter. + * + * @author Alan Davis + */ +public class LogAdapterTest +{ + @Mock + Log log; + + LogAdapter adapter; + + Throwable throwable; + + @Before + public void setUp() throws Exception + { + MockitoAnnotations.initMocks(this); + + adapter = new LogAdapter(log) { }; + throwable = new Exception(); + } + + @Test + public void traceTest() + { + adapter.trace(""); + adapter.trace("", throwable); + verify(log).trace("", null); + verify(log).trace("", throwable); + + when(log.isTraceEnabled()).thenReturn(true); + assertTrue("", adapter.isTraceEnabled()); + + when(log.isTraceEnabled()).thenReturn(false); + assertFalse("", adapter.isTraceEnabled()); + } + + @Test + public void debugTest() + { + adapter.debug(""); + adapter.debug("", throwable); + verify(log).debug("", null); + verify(log).debug("", throwable); + + when(log.isDebugEnabled()).thenReturn(true); + assertTrue("", adapter.isDebugEnabled()); + + when(log.isDebugEnabled()).thenReturn(false); + assertFalse("", adapter.isDebugEnabled()); + } + + @Test + public void infoTest() + { + adapter.info(""); + adapter.info("", throwable); + verify(log).info("", null); + verify(log).info("", throwable); + + when(log.isInfoEnabled()).thenReturn(true); + assertTrue("", adapter.isInfoEnabled()); + + when(log.isInfoEnabled()).thenReturn(false); + assertFalse("", adapter.isInfoEnabled()); + } + + @Test + public void warnTest() + { + adapter.warn(""); + adapter.warn("", throwable); + verify(log).warn("", null); + verify(log).warn("", throwable); + + when(log.isWarnEnabled()).thenReturn(true); + assertTrue("", adapter.isWarnEnabled()); + + when(log.isWarnEnabled()).thenReturn(false); + assertFalse("", adapter.isWarnEnabled()); + } + + @Test + public void errorTest() + { + adapter.error(""); + adapter.error("", throwable); + verify(log).error("", null); + verify(log).error("", throwable); + + when(log.isErrorEnabled()).thenReturn(true); + assertTrue("", adapter.isErrorEnabled()); + + when(log.isErrorEnabled()).thenReturn(false); + assertFalse("", adapter.isErrorEnabled()); + } + + @Test + public void fatalTest() + { + adapter.fatal(""); + adapter.fatal("", throwable); + verify(log).fatal("", null); + verify(log).fatal("", throwable); + + when(log.isFatalEnabled()).thenReturn(true); + assertTrue("", adapter.isFatalEnabled()); + + when(log.isFatalEnabled()).thenReturn(false); + assertFalse("", adapter.isFatalEnabled()); + } + + @Test + public void nullTest() + { + adapter = new LogAdapter(null) { }; + + adapter.trace(""); + adapter.trace("", throwable); + adapter.debug(""); + adapter.debug("", throwable); + adapter.info(""); + adapter.info("", throwable); + adapter.warn(""); + adapter.warn("", throwable); + adapter.error(""); + adapter.error("", throwable); + adapter.fatal(""); + adapter.fatal("", throwable); + verify(log, times(0)).trace("", null); + verify(log, times(0)).trace("", throwable); + verify(log, times(0)).debug("", null); + verify(log, times(0)).debug("", throwable); + verify(log, times(0)).info("", null); + verify(log, times(0)).info("", throwable); + verify(log, times(0)).warn("", null); + verify(log, times(0)).warn("", throwable); + verify(log, times(0)).error("", null); + verify(log, times(0)).error("", throwable); + verify(log, times(0)).fatal("", null); + verify(log, times(0)).fatal("", throwable); + + when(log.isTraceEnabled()).thenReturn(true); + assertFalse("", adapter.isTraceEnabled()); + + when(log.isTraceEnabled()).thenReturn(false); + assertFalse("", adapter.isTraceEnabled()); + + when(log.isDebugEnabled()).thenReturn(true); + assertFalse("", adapter.isDebugEnabled()); + + when(log.isDebugEnabled()).thenReturn(false); + assertFalse("", adapter.isDebugEnabled()); + + when(log.isInfoEnabled()).thenReturn(true); + assertFalse("", adapter.isInfoEnabled()); + + when(log.isInfoEnabled()).thenReturn(false); + assertFalse("", adapter.isInfoEnabled()); + + when(log.isWarnEnabled()).thenReturn(true); + assertFalse("", adapter.isWarnEnabled()); + + when(log.isWarnEnabled()).thenReturn(false); + assertFalse("", adapter.isWarnEnabled()); + + when(log.isErrorEnabled()).thenReturn(true); + assertFalse("", adapter.isErrorEnabled()); + + when(log.isErrorEnabled()).thenReturn(false); + assertFalse("", adapter.isErrorEnabled()); + + when(log.isFatalEnabled()).thenReturn(true); + assertFalse("", adapter.isFatalEnabled()); + + when(log.isFatalEnabled()).thenReturn(false); + assertFalse("", adapter.isFatalEnabled()); + } +} diff --git a/src/test/java/org/alfresco/util/LogTeeTest.java b/src/test/java/org/alfresco/util/LogTeeTest.java new file mode 100644 index 0000000000..660fc1e812 --- /dev/null +++ b/src/test/java/org/alfresco/util/LogTeeTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.commons.logging.Log; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test class for LogTee. + * + * @author Alan Davis + */ +public class LogTeeTest +{ + @Mock + Log log1; + + @Mock + Log log2; + + LogTee tee; + + Throwable throwable; + + @Before + public void setUp() throws Exception + { + MockitoAnnotations.initMocks(this); + + tee = new LogTee(log1, log2) { }; + throwable = new Exception(); + } + + @Test + public void traceTest() + { + tee.trace(""); + tee.trace("", throwable); + verify(log1).trace("", null); + verify(log1).trace("", throwable); + verify(log2).trace("", null); + verify(log2).trace("", throwable); + + when(log1.isTraceEnabled()).thenReturn(true); + assertTrue("", tee.isTraceEnabled()); + + when(log2.isTraceEnabled()).thenReturn(true); + assertTrue("", tee.isTraceEnabled()); + + when(log1.isTraceEnabled()).thenReturn(false); + assertTrue("", tee.isTraceEnabled()); + + when(log2.isTraceEnabled()).thenReturn(false); + assertFalse("", tee.isTraceEnabled()); + + when(log2.isTraceEnabled()).thenReturn(true); + assertTrue("", tee.isTraceEnabled()); + } + + @Test + public void debugTest() + { + tee.debug(""); + tee.debug("", throwable); + verify(log1).debug("", null); + verify(log1).debug("", throwable); + verify(log2).debug("", null); + verify(log2).debug("", throwable); + + + when(log1.isDebugEnabled()).thenReturn(true); + assertTrue("", tee.isDebugEnabled()); + + when(log2.isDebugEnabled()).thenReturn(true); + assertTrue("", tee.isDebugEnabled()); + + when(log1.isDebugEnabled()).thenReturn(false); + assertTrue("", tee.isDebugEnabled()); + + when(log2.isDebugEnabled()).thenReturn(false); + assertFalse("", tee.isDebugEnabled()); + + when(log2.isDebugEnabled()).thenReturn(true); + assertTrue("", tee.isDebugEnabled()); + } + + @Test + public void infoTest() + { + tee.info(""); + tee.info("", throwable); + verify(log1).info("", null); + verify(log1).info("", throwable); + verify(log2).info("", null); + verify(log2).info("", throwable); + + + when(log1.isInfoEnabled()).thenReturn(true); + assertTrue("", tee.isInfoEnabled()); + + when(log2.isInfoEnabled()).thenReturn(true); + assertTrue("", tee.isInfoEnabled()); + + when(log1.isInfoEnabled()).thenReturn(false); + assertTrue("", tee.isInfoEnabled()); + + when(log2.isInfoEnabled()).thenReturn(false); + assertFalse("", tee.isInfoEnabled()); + + when(log2.isInfoEnabled()).thenReturn(true); + assertTrue("", tee.isInfoEnabled()); + } + + @Test + public void warnTest() + { + tee.warn(""); + tee.warn("", throwable); + verify(log1).warn("", null); + verify(log1).warn("", throwable); + verify(log2).warn("", null); + verify(log2).warn("", throwable); + + + when(log1.isWarnEnabled()).thenReturn(true); + assertTrue("", tee.isWarnEnabled()); + + when(log2.isWarnEnabled()).thenReturn(true); + assertTrue("", tee.isWarnEnabled()); + + when(log1.isWarnEnabled()).thenReturn(false); + assertTrue("", tee.isWarnEnabled()); + + when(log2.isWarnEnabled()).thenReturn(false); + assertFalse("", tee.isWarnEnabled()); + + when(log2.isWarnEnabled()).thenReturn(true); + assertTrue("", tee.isWarnEnabled()); + } + + @Test + public void errorTest() + { + tee.error(""); + tee.error("", throwable); + verify(log1).error("", null); + verify(log1).error("", throwable); + verify(log2).error("", null); + verify(log2).error("", throwable); + + + when(log1.isErrorEnabled()).thenReturn(true); + assertTrue("", tee.isErrorEnabled()); + + when(log2.isErrorEnabled()).thenReturn(true); + assertTrue("", tee.isErrorEnabled()); + + when(log1.isErrorEnabled()).thenReturn(false); + assertTrue("", tee.isErrorEnabled()); + + when(log2.isErrorEnabled()).thenReturn(false); + assertFalse("", tee.isErrorEnabled()); + + when(log2.isErrorEnabled()).thenReturn(true); + assertTrue("", tee.isErrorEnabled()); + } + + @Test + public void fatalTest() + { + tee.fatal(""); + tee.fatal("", throwable); + verify(log1).fatal("", null); + verify(log1).fatal("", throwable); + verify(log2).fatal("", null); + verify(log2).fatal("", throwable); + + + when(log1.isFatalEnabled()).thenReturn(true); + assertTrue("", tee.isFatalEnabled()); + + when(log2.isFatalEnabled()).thenReturn(true); + assertTrue("", tee.isFatalEnabled()); + + when(log1.isFatalEnabled()).thenReturn(false); + assertTrue("", tee.isFatalEnabled()); + + when(log2.isFatalEnabled()).thenReturn(false); + assertFalse("", tee.isFatalEnabled()); + + when(log2.isFatalEnabled()).thenReturn(true); + assertTrue("", tee.isFatalEnabled()); + } +} diff --git a/src/test/java/org/alfresco/util/PathMapperTest.java b/src/test/java/org/alfresco/util/PathMapperTest.java new file mode 100644 index 0000000000..f9455e1edd --- /dev/null +++ b/src/test/java/org/alfresco/util/PathMapperTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +/** + * @see PathMapper + * + * @author Derek Hulley + * @since 3.2 + */ +public class PathMapperTest extends TestCase +{ + private PathMapper mapper; + + @Override + protected void setUp() throws Exception + { + mapper = new PathMapper(); + mapper.addPathMap("/a/b/c", "/1/2/3"); + mapper.addPathMap("/a/b/c", "/one/two/three"); + mapper.addPathMap("/a/c/c", "/1/3/3"); + mapper.addPathMap("/a/c/c", "/one/three/three"); + mapper.addPathMap("/A/B/C", "/1/2/3"); + mapper.addPathMap("/A/B/C", "/ONE/TWO/THREE"); + mapper.addPathMap("/A/C/C", "/1/3/3"); + mapper.addPathMap("/A/C/C", "/ONE/THREE/THREE"); + } + + public void testConvertValueMap() + { + Map inputMap = new HashMap(5); + inputMap.put("/a/a/a/111", 111); + inputMap.put("/a/b/c/123", 123); + inputMap.put("/a/b/b/122", 122); + inputMap.put("/a/c/c/133", 133); + inputMap.put("/A/A/A/111", 111); + inputMap.put("/A/B/C/123", 123); + inputMap.put("/A/B/B/122", 122); + inputMap.put("/A/C/C/133", 133); + + Map expectedOutputMap = new HashMap(5); + expectedOutputMap.put("/1/2/3/123", 123); + expectedOutputMap.put("/one/two/three/123", 123); + expectedOutputMap.put("/1/3/3/133", 133); + expectedOutputMap.put("/one/three/three/133", 133); + expectedOutputMap.put("/1/2/3/123", 123); + expectedOutputMap.put("/ONE/TWO/THREE/123", 123); + expectedOutputMap.put("/1/3/3/133", 133); + expectedOutputMap.put("/ONE/THREE/THREE/133", 133); + + Map outputMap = mapper.convertMap(inputMap); + + String diff = EqualsHelper.getMapDifferenceReport(outputMap, expectedOutputMap); + if (diff != null) + { + fail(diff); + } + } + + public void testPathMatchesExact() + { + Set mappedPaths = mapper.getMappedPaths("/a/b/c"); + assertEquals("Exact matches expected", 2, mappedPaths.size()); + mappedPaths = mapper.getMappedPaths("/a"); + assertEquals("Exact match NOT expected", 0, mappedPaths.size()); + } + + public void testPathMatchesPartial() + { + Set mappedPaths = mapper.getMappedPathsWithPartialMatch("/a"); + assertEquals("Partial matches expected", 4, mappedPaths.size()); + } +} diff --git a/src/test/java/org/alfresco/util/TempFileProviderTest.java b/src/test/java/org/alfresco/util/TempFileProviderTest.java new file mode 100644 index 0000000000..cd5b1c8358 --- /dev/null +++ b/src/test/java/org/alfresco/util/TempFileProviderTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import java.io.File; + +import junit.framework.TestCase; + +/** + * Unit test for TempFileProvider + * + * @see org.alfresco.util.TempFileProvider + * + * @author Derek Hulley + */ +public class TempFileProviderTest extends TestCase +{ + /** + * test of getTempDir + * + * @throws Exception + */ + public void testTempDir() throws Exception + { + File tempDir = TempFileProvider.getTempDir(); + assertTrue("Not a directory", tempDir.isDirectory()); + File tempDirParent = tempDir.getParentFile(); + + // create a temp file + File tempFile = File.createTempFile("AAAA", ".tmp"); + File tempFileParent = tempFile.getParentFile(); + + // they should be equal + assertEquals("Our temp dir not subdirectory system temp directory", + tempFileParent, tempDirParent); + } + + /** + * test create a temporary file + * + * create another file with the same prefix and suffix. + * @throws Exception + */ + public void testTempFile() throws Exception + { + File tempFile = TempFileProvider.createTempFile("AAAA", ".tmp"); + File tempFileParent = tempFile.getParentFile(); + File tempDir = TempFileProvider.getTempDir(); + assertEquals("Temp file not located in our temp directory", + tempDir, tempFileParent); + + /** + * Create another temp file and then delete it. + */ + File tempFile2 = TempFileProvider.createTempFile("AAAA", ".tmp"); + tempFile2.delete(); + } + + /** + * test create a temporary file with a directory + * + * create another file with the same prefix and suffix. + * @throws Exception + */ + public void testTempFileWithDir() throws Exception + { + File tempDir = TempFileProvider.getTempDir(); + File tempFile = TempFileProvider.createTempFile("AAAA", ".tmp", tempDir); + File tempFileParent = tempFile.getParentFile(); + assertEquals("Temp file not located in our temp directory", + tempDir, tempFileParent); + + /** + * Create another temp file and then delete it. + */ + File tempFile2 = TempFileProvider.createTempFile("AAAA", ".tmp", tempDir); + tempFile2.delete(); + } +} diff --git a/src/test/java/org/alfresco/util/VersionNumberTest.java b/src/test/java/org/alfresco/util/VersionNumberTest.java new file mode 100644 index 0000000000..3a1bd79b60 --- /dev/null +++ b/src/test/java/org/alfresco/util/VersionNumberTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import junit.framework.TestCase; + +/** + * Test for extension version class. + * + * @author Roy Wetherall + */ +public class VersionNumberTest extends TestCase +{ + public void testCreate() + { + VersionNumber version1 = new VersionNumber("1"); + int[] parts1 = version1.getParts(); + assertNotNull(parts1); + assertEquals(1, parts1.length); + assertEquals(1, parts1[0]); + + VersionNumber version2 = new VersionNumber("1.2"); + int[] parts2 = version2.getParts(); + assertNotNull(parts2); + assertEquals(2, parts2.length); + assertEquals(1, parts2[0]); + assertEquals(2, parts2[1]); + + VersionNumber version3 = new VersionNumber("1.2.3"); + int[] parts3 = version3.getParts(); + assertNotNull(parts3); + assertEquals(3, parts3.length); + assertEquals(1, parts3[0]); + assertEquals(2, parts3[1]); + assertEquals(3, parts3[2]); + + try + { + new VersionNumber("xxx"); + fail("Should not have created an invalid version"); + } catch (Exception exception) + { + // OK + } + try + { + new VersionNumber("1-1-2"); + fail("Should not have created an invalid version"); + } catch (Exception exception) + { + // OK + } + try + { + new VersionNumber("1.2.3a"); + fail("Should not have created an invalid version"); + } catch (Exception exception) + { + // OK + } + } + + public void testEquals() + { + VersionNumber version0 = new VersionNumber("1"); + VersionNumber version1 = new VersionNumber("1.2"); + VersionNumber version2 = new VersionNumber("1.2"); + VersionNumber version3 = new VersionNumber("1.2.3"); + VersionNumber version4 = new VersionNumber("1.2.3"); + VersionNumber version5 = new VersionNumber("1.3.3"); + VersionNumber version6 = new VersionNumber("1.0"); + + assertFalse(version0.equals(version1)); + assertTrue(version1.equals(version2)); + assertFalse(version2.equals(version3)); + assertTrue(version3.equals(version4)); + assertFalse(version4.equals(version5)); + assertTrue(version0.equals(version6)); + } + + public void testCompare() + { + VersionNumber version0 = new VersionNumber("1"); + VersionNumber version1 = new VersionNumber("1.2"); + VersionNumber version2 = new VersionNumber("1.2"); + VersionNumber version3 = new VersionNumber("1.2.3"); + VersionNumber version4 = new VersionNumber("1.11"); + VersionNumber version5 = new VersionNumber("1.3.3"); + VersionNumber version6 = new VersionNumber("2.0"); + VersionNumber version7 = new VersionNumber("2.0.1"); + VersionNumber version8 = new VersionNumber("10.0"); + VersionNumber version9 = new VersionNumber("10.3"); + VersionNumber version10 = new VersionNumber("11.1"); + + assertEquals(-1, version0.compareTo(version1)); + assertEquals(1, version1.compareTo(version0)); + assertEquals(0, version1.compareTo(version2)); + assertEquals(-1, version2.compareTo(version3)); + assertEquals(-1, version2.compareTo(version4)); + assertEquals(-1, version3.compareTo(version5)); + assertEquals(1, version6.compareTo(version5)); + assertEquals(-1, version6.compareTo(version7)); + assertEquals(-1, version1.compareTo(version8)); + assertEquals(-1, version8.compareTo(version9)); + assertEquals(-1, version9.compareTo(version10)); + } +} diff --git a/src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java b/src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java new file mode 100644 index 0000000000..1cd11a6022 --- /dev/null +++ b/src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.bean; + +import java.util.AbstractCollection; +import java.util.AbstractList; +import java.util.Collection; +import java.util.TreeSet; + +import junit.framework.TestCase; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @see HierarchicalBeanLoader + * + * @author Derek Hulley + * @since 3.2SP1 + */ +public class HierarchicalBeanLoaderTest extends TestCase +{ + private ClassPathXmlApplicationContext ctx; + + private String getBean(Class clazz, boolean setBeforeInit) throws Exception + { + if (setBeforeInit) + { + System.setProperty("hierarchy-test.dialect", clazz.getName()); + } + ctx = new ClassPathXmlApplicationContext("bean-loader/hierarchical-bean-loader-test-context.xml"); + if (!setBeforeInit) + { + System.setProperty("hierarchy-test.dialect", clazz.getName()); + } + return (String) ctx.getBean("test.someString"); + } + + public void tearDown() + { + try + { + ctx.close(); + } + catch (Throwable e) + { + } + } + + public void testSuccess1() throws Throwable + { + String str = getBean(TreeSet.class, true); + assertEquals("Bean value incorrect", "TreeSet", str); + } + + public void testSuccess2() throws Throwable + { + String str = getBean(AbstractList.class, true); + assertEquals("Bean value incorrect", "AbstractList", str); + } + + public void testSuccess3() throws Throwable + { + String str = getBean(AbstractCollection.class, true); + assertEquals("Bean value incorrect", "AbstractCollection", str); + } + + public void testFailure1() throws Throwable + { + try + { + getBean(Collection.class, true); + fail("Should not be able to retrieve bean using class " + Collection.class); + } + catch (Throwable e) + { + e.printStackTrace(); + } + } +} diff --git a/src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java b/src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java new file mode 100644 index 0000000000..18ac6af79a --- /dev/null +++ b/src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.collections; + +import static java.util.Arrays.asList; + +import static org.alfresco.util.collections.CollectionUtils.asSet; +import static org.alfresco.util.collections.CollectionUtils.nullSafeMerge; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for {@link CollectionUtils}. + * + * @author Neil Mc Erlean + */ +public class CollectionUtilsTest +{ + private static Set stooges; + + private static Map primes; + private static Map squares; + private static Map nullMap; + private static Map nerdsBirthdays; + + @Before public void initData() + { + stooges = new HashSet<>(); + stooges.add("Larry"); + stooges.add("Curly"); + stooges.add("Moe"); + + primes = new HashMap<>(); + primes.put("two", 2); + primes.put("three", 3); + primes.put("five", 5); + + squares = new HashMap<>(); + squares.put("one", 1); + squares.put("two", 4); + squares.put("three", 9); + + nerdsBirthdays = new HashMap<>(); + nerdsBirthdays.put("Alan Turing", 1912); + nerdsBirthdays.put("Charles Babbage", 1791); + nerdsBirthdays.put("Matthew Smith", 1966); + nerdsBirthdays.put("Paul Dirac", 1902); + nerdsBirthdays.put("Robert Boyle", 1627); + nerdsBirthdays.put("Robert Hooke", 1635); + nerdsBirthdays.put("J. Robert Oppenheimer", 1904); + } + + @Test public void varArgsAsSet() { + assertEquals(stooges, asSet("Larry", "Curly", "Moe")); + + assertEquals(stooges, CollectionUtils.asSet(String.class, "Larry", "Curly", "Moe")); + } + + @Test public void nullSafeMergeMaps() + { + assertNull(nullSafeMerge(nullMap, nullMap, true)); + + assertEquals(Collections.emptyMap(), nullSafeMerge(nullMap, nullMap)); + assertEquals(primes, nullSafeMerge(nullMap, primes)); + assertEquals(primes, nullSafeMerge(primes, nullMap)); + + Map primesAndSquares = new HashMap<>(); + primesAndSquares.putAll(primes); + primesAndSquares.putAll(squares); + + assertEquals(primesAndSquares, nullSafeMerge(primes, squares)); + } + + @Test public void collectionFiltering() throws Exception + { + Function johnFilter = new KeySubstringFilter("John"); + assertEquals(0, CollectionUtils.filterKeys(nerdsBirthdays, johnFilter).size()); + + Function robertFilter = new KeySubstringFilter("Robert"); + assertEquals(3, CollectionUtils.filterKeys(nerdsBirthdays, robertFilter).size()); + } + + private static final class KeySubstringFilter implements Function + { + private final String substring; + public KeySubstringFilter(String substring) { this.substring = substring; } + @Override public Boolean apply(String value) + { + return value.contains(substring); + } + } + + @Test public void sortMapsByEntry() throws Exception + { + final Map expectedSorting = getNerdsSortedByBirthDate(); + + Comparator> entryComparator = new Comparator>() + { + @Override public int compare(Entry e1, Entry e2) + { + return e1.getValue().intValue() - e2.getValue().intValue(); + } + }; + + final Map actualSorting = CollectionUtils.sortMapByValue(nerdsBirthdays, entryComparator); + + assertEquals(expectedSorting, actualSorting); + } + + @Test public void sortMapsByValue() throws Exception + { + final Map expectedSorting = getNerdsSortedByBirthDate(); + + Comparator valueComparator = new Comparator() + { + @Override public int compare(Integer i1, Integer i2) + { + return i1.intValue() - i2.intValue(); + } + }; + + Comparator> entryComparator = CollectionUtils.toEntryComparator(valueComparator); + + final Map actualSorting = CollectionUtils.sortMapByValue(nerdsBirthdays, entryComparator); + + assertEquals(expectedSorting, actualSorting); + } + + private Map getNerdsSortedByBirthDate() + { + final Map result = new LinkedHashMap<>(); // maintains insertion order + result.put("Robert Boyle", 1627); + result.put("Robert Hooke", 1635); + result.put("Charles Babbage", 1791); + result.put("Paul Dirac", 1902); + result.put("J. Robert Oppenheimer", 1904); + result.put("Alan Turing", 1912); + result.put("Matthew Smith", 1966); + return result; + } + + @Test public void moveItemInList() throws Exception + { + final List input = asList("a", "b", "c"); + + assertEquals(asList("a", "b", "c"), CollectionUtils.moveRight(0, "b", input)); + assertEquals(asList("a", "c", "b"), CollectionUtils.moveRight(1, "b", input)); + assertEquals(asList("a", "c", "b"), CollectionUtils.moveRight(5, "b", input)); + + assertEquals(asList("c", "a", "b"), CollectionUtils.moveRight(-2, "c", input)); + assertEquals(asList("c", "a", "b"), CollectionUtils.moveRight(-5, "c", input)); + + assertEquals(asList("a", "b", "c"), CollectionUtils.moveLeft(0, "b", input)); + assertEquals(asList("b", "a", "c"), CollectionUtils.moveLeft(1, "b", input)); + assertEquals(asList("b", "a", "c"), CollectionUtils.moveLeft(5, "b", input)); + + assertEquals(asList("b", "c", "a"), CollectionUtils.moveLeft(-2, "a", input)); + assertEquals(asList("b", "c", "a"), CollectionUtils.moveLeft(-5, "a", input)); + + try { CollectionUtils.moveRight(1, "x", input); } + catch (NoSuchElementException expected) { return; } + + fail("Expected exception was not thrown."); + } + + @Test public void flattenCollections() throws Exception + { + final List list1 = CollectionUtils.toListOfStrings(stooges); + Collections.sort(list1); + final List list2 = asList("Hello", "World"); + + assertEquals(asList("Curly", "Larry", "Moe", "Hello", "World"), + CollectionUtils.flatten(list1, list2)); + } +} diff --git a/src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java b/src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java new file mode 100644 index 0000000000..c2d660da46 --- /dev/null +++ b/src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.exec; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +/** + * Unit test class for {@link ExecParameterTokenizer}. + * + * @author Neil Mc Erlean + * @since 3.4.2 + */ +public class ExecParameterTokenizerTest +{ + @Test public void tokenizeEmptyString() + { + final String str1 = ""; + final String str2 = " \t "; + + List expectedTokens = Arrays.asList(new String[0]); + + ExecParameterTokenizer t = new ExecParameterTokenizer(str1); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + + t = new ExecParameterTokenizer(str2); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + } + + @Test(expected=NullPointerException.class) public void tokenizeNullString() + { + final String str1 = null; + + List expectedTokens = Arrays.asList(new String[0]); + + ExecParameterTokenizer t = new ExecParameterTokenizer(str1); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + } + + @Test public void tokenizeSimpleParameterString() + { + final String str1 = "-font Helvetica -pointsize 50"; + final String str2 = " -font Helvetica -pointsize 50 "; + + List expectedTokens = Arrays.asList(new String[] {"-font", "Helvetica", "-pointsize", "50"}); + + ExecParameterTokenizer t = new ExecParameterTokenizer(str1); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + + t = new ExecParameterTokenizer(str2); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + } + + @Test public void tokenizeParameterStringEntirelyQuoted() + { + final String str1 = "\"circle 100,100 150,150\""; + final String str2 = "'circle 100,100 150,150'"; + + List expectedTokens = Arrays.asList(new String[] {"circle 100,100 150,150"}); + + ExecParameterTokenizer t = new ExecParameterTokenizer(str1); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + + t = new ExecParameterTokenizer(str2); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + } + + @Test(expected=IllegalArgumentException.class) + public void tokenizeParameterStringWithUnclosedSingleQuote() + { + final String str = "-font Helvetica -pointsize 50 -draw 'circle"; + + ExecParameterTokenizer t = new ExecParameterTokenizer(str); + t.getAllTokens(); + } + + @Test(expected=IllegalArgumentException.class) + public void tokenizeParameterStringWithUnclosedDoubleQuote() + { + final String str = "-font Helvetica -pointsize 50 -draw \"circle"; + + ExecParameterTokenizer t = new ExecParameterTokenizer(str); + t.getAllTokens(); + } + + @Test(expected=IllegalArgumentException.class) + public void tokenizeParameterStringWithMalformedQuoteNesting() + { + final String str = " \"foo 'bar baz\" hello' "; + + ExecParameterTokenizer t = new ExecParameterTokenizer(str); + t.getAllTokens(); + } + + @Test public void tokenizeParameterStringWithQuotedParam() + { + final String str1 = "-font Helvetica -pointsize 50 -draw \"circle 100,100 150,150\""; + final String str2 = "-font Helvetica -pointsize 50 -draw 'circle 100,100 150,150'"; + + List expectedTokens = Arrays.asList(new String[] {"-font", "Helvetica", "-pointsize", "50", + "-draw", "circle 100,100 150,150"}); + + ExecParameterTokenizer t = new ExecParameterTokenizer(str1); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + + t = new ExecParameterTokenizer(str2); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + } + + @Test public void tokenizeParameterStringWithQuotedParam_MixedQuotes() + { + final String str1 = "'Hello world' middle \"Goodbye world\""; + final String str2 = "\"Hello world\" middle 'Goodbye world'"; + + List expectedTokens = Arrays.asList(new String[] {"Hello world", "middle", "Goodbye world"}); + + ExecParameterTokenizer t = new ExecParameterTokenizer(str1); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + + t = new ExecParameterTokenizer(str2); + assertEquals("Wrong tokens", expectedTokens, t.getAllTokens()); + } + + @Test public void tokenizeParameterStringWithQuotedParamContainingQuotes() + { + final String str1 = "-font Helvetica -pointsize 50 -draw \"gravity south fill black text 0,12 'CopyRight'\""; + final String str2 = "-font Helvetica -pointsize 50 -draw 'gravity south fill black text 0,12 \"CopyRight\"'"; + + List expectedTokens1 = Arrays.asList(new String[] {"-font", "Helvetica", "-pointsize", "50", + "-draw", "gravity south fill black text 0,12 'CopyRight'"}); + + List expectedTokens2 = Arrays.asList(new String[] {"-font", "Helvetica", "-pointsize", "50", + "-draw", "gravity south fill black text 0,12 \"CopyRight\""}); + + ExecParameterTokenizer t = new ExecParameterTokenizer(str1); + assertEquals("Wrong tokens", expectedTokens1, t.getAllTokens()); + + t = new ExecParameterTokenizer(str2); + assertEquals("Wrong tokens", expectedTokens2, t.getAllTokens()); + } +} diff --git a/src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java b/src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java new file mode 100644 index 0000000000..464d980ba3 --- /dev/null +++ b/src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.exec; + +import java.io.File; +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.alfresco.util.exec.RuntimeExec.ExecutionResult; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @see org.alfresco.util.exec.RuntimeExecBootstrapBean + * + * @author Derek Hulley + */ +public class RuntimeExecBeansTest extends TestCase +{ + private static Log logger = LogFactory.getLog(RuntimeExecBeansTest.class); + + private static final String APP_CONTEXT_XML = + "classpath:org/alfresco/util/exec/RuntimeExecBeansTest-context.xml"; + private static final String DIR = "dir RuntimeExecBootstrapBeanTest"; + + private File dir; + + public void setUp() throws Exception + { + dir = new File(DIR); + dir.mkdir(); + assertTrue("Directory not created", dir.exists()); + } + + public void testBootstrapAndShutdown() throws Exception + { + // now bring up the bootstrap + ApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); + + // the folder should be gone + assertFalse("Folder was not deleted by bootstrap", dir.exists()); + + // now create the folder again + dir.mkdir(); + assertTrue("Directory not created", dir.exists()); + + // announce that the context is closing + ctx.publishEvent(new ContextClosedEvent(ctx)); + + // the folder should be gone + assertFalse("Folder was not deleted by shutdown", dir.exists()); + } + + public void testSimpleSuccess() throws Exception + { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); + try + { + RuntimeExec dirRootExec = (RuntimeExec) ctx.getBean("commandListRootDir"); + assertNotNull(dirRootExec); + // Execute it + dirRootExec.execute(); + } + finally + { + ctx.close(); + } + } + + public void testDeprecatedSetCommandMap() throws Exception + { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); + try + { + RuntimeExec deprecatedExec = (RuntimeExec) ctx.getBean("commandCheckDeprecatedSetCommandMap"); + assertNotNull(deprecatedExec); + // Execute it + deprecatedExec.execute(); + } + finally + { + ctx.close(); + } + // The best we can do is look at the log manually + logger.warn("There should be a warning re. the use of deprecated 'setCommandMap'."); + } + + public void testSplitArguments() throws Exception + { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); + try + { + RuntimeExec splitExec = (RuntimeExec) ctx.getBean("commandSplitArguments"); + assertNotNull(splitExec); + String[] splitCommand = splitExec.getCommand(); + assertTrue( + "Command arguments not split into 'dir', '.' and '..' :" + Arrays.deepToString(splitCommand), + Arrays.deepEquals(new String[] {"dir", ".", ".."}, splitCommand)); + } + finally + { + ctx.close(); + } + } + + public void testSplitArgumentsAsSingleValue() throws Exception + { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); + try + { + RuntimeExec splitExec = (RuntimeExec) ctx.getBean("commandSplitArgumentsAsSingleValue"); + assertNotNull(splitExec); + String[] splitCommand = splitExec.getCommand(); + assertTrue( + "Command arguments not split into 'dir', '.' and '..' : " + Arrays.deepToString(splitCommand), + Arrays.deepEquals(new String[] {"dir", ".", ".."}, splitCommand)); + } + finally + { + ctx.close(); + } + } + + public void testFailureModeOfMissingCommand() + { + File dir = new File(DIR); + dir.mkdir(); + assertTrue("Directory not created", dir.exists()); + + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); + try + { + RuntimeExec failureExec = (RuntimeExec) ctx.getBean("commandFailureGuaranteed"); + assertNotNull(failureExec); + // Execute it + ExecutionResult result = failureExec.execute(); + assertEquals("Expected first error code in list", 666, result.getExitValue()); + } + finally + { + ctx.close(); + } + } + +// /** +// * Checks that the encoding setting feeds through to the streams. +// */ +// public void testStreamReading() throws Exception +// { +// String manglingCharsetName = "UTF-16"; +// +// File dir = new File(DIR); +// dir.mkdir(); +// assertTrue("Directory not created", dir.exists()); +// +// ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); +// try +// { +// RuntimeExec dirRootExec = (RuntimeExec) ctx.getBean("commandListRootDir"); +// assertNotNull(dirRootExec); +// // Execute it +// ExecutionResult result = dirRootExec.execute(); +// +// // Get the error stream +// String defaultStdOut = result.getStdOut(); +// +// // Change the encoding +// dirRootExec.setCharset(manglingCharsetName); +// result = dirRootExec.execute(); +// String mangledStdOut = result.getStdOut(); +// // The two error strings must not be the same +// assertNotSame("Differently encoded strings should differ", defaultStdOut, mangledStdOut); +// +// // Now convert the Shift-JIS string and ensure it's the same as originally expected +// Charset defaultCharset = Charset.defaultCharset(); +// byte[] mangledBytes = mangledStdOut.getBytes(manglingCharsetName); +// String convertedStrOut = new String(mangledBytes, defaultCharset.name()); +// // Check, catering for any mangled characters +// assertTrue("Expected to be able to convert value back to default charset.", convertedStrOut.contains(defaultStdOut)); +// } +// finally +// { +// ctx.close(); +// } +// } +// + public void testExecOfNeverEndingProcess() + { + File dir = new File(DIR); + dir.mkdir(); + assertTrue("Directory not created", dir.exists()); + + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(APP_CONTEXT_XML); + try + { + RuntimeExec failureExec = (RuntimeExec) ctx.getBean("commandNeverEnding"); + assertNotNull(failureExec); + // Execute it + failureExec.execute(); + // The command is never-ending, so this should be out immediately + } + finally + { + ctx.close(); + } + } +} diff --git a/src/test/java/org/alfresco/util/exec/RuntimeExecTest.java b/src/test/java/org/alfresco/util/exec/RuntimeExecTest.java new file mode 100644 index 0000000000..9144a2fb53 --- /dev/null +++ b/src/test/java/org/alfresco/util/exec/RuntimeExecTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.exec; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.util.exec.RuntimeExec.ExecutionResult; + +import junit.framework.TestCase; + +/** + * @see org.alfresco.util.exec.RuntimeExec + * + * @author Derek Hulley + */ +public class RuntimeExecTest extends TestCase +{ + public void testStreams() throws Exception + { + RuntimeExec exec = new RuntimeExec(); + + // This test will return different results on Windows and Linux! + // note that some Unix variants will error without a path + HashMap commandMap = new HashMap(5); + commandMap.put("*", new String[] {"find", "/", "-maxdepth", "1", "-name", "var"}); + commandMap.put("Windows.*", new String[] {"find", "/?"}); + exec.setCommandsAndArguments(commandMap); + // execute + ExecutionResult ret = exec.execute(); + + String out = ret.getStdOut(); + String err = ret.getStdErr(); + + assertEquals("Didn't expect error code", 0, ret.getExitValue()); + assertEquals("Didn't expect any error output", 0, err.length()); + assertTrue("No output found", out.length() > 0); + } + + public void testWildcard() throws Exception + { + RuntimeExec exec = new RuntimeExec(); + + // set the command + Map commandMap = new HashMap(3, 1.0f); + commandMap.put(".*", (new String[]{"TEST"})); + exec.setCommandsAndArguments(commandMap); + + String[] commandStr = exec.getCommand(); + assertTrue("Expected default match to work", Arrays.deepEquals(new String[] {"TEST"}, commandStr)); + } + + public void testWithProperties() throws Exception + { + RuntimeExec exec = new RuntimeExec(); + + // set the command + Map commandMap = new HashMap(3, 1.0f); + commandMap.put("Windows.*", new String[]{"dir", "${path}"}); + commandMap.put("Linux", new String[] {"ls", "${path}"}); + commandMap.put("Mac OS X", new String[]{"ls", "${path}"}); + commandMap.put("*", new String[]{"wibble", "${path}"}); + exec.setCommandsAndArguments(commandMap); + + // set the default properties + Map defaultProperties = new HashMap(1, 1.0f); + defaultProperties.put("path", "."); + exec.setDefaultProperties(defaultProperties); + + // check that the command lines generated are correct + String defaultCommand[] = exec.getCommand(); + String dynamicCommand[] = exec.getCommand(Collections.singletonMap("path", "./")); + // check + String os = System.getProperty("os.name"); + String[] defaultCommandCheck = null; + String[] dynamicCommandCheck = null; + if (os.matches("Windows.*")) + { + defaultCommandCheck = new String[]{"dir", "."}; + dynamicCommandCheck = new String[]{"dir", "./"}; + } + else if (os.equals("Linux") || os.equals("Mac OS X")) + { + defaultCommandCheck = new String[]{"ls", "."}; + dynamicCommandCheck = new String[]{"ls", "./"}; + } + else + { + defaultCommandCheck = new String[]{"wibble", "."}; + dynamicCommandCheck = new String[]{"wibble", "./"}; + } + assertTrue("Default command for OS " + os + " is incorrect", Arrays.deepEquals(defaultCommandCheck, defaultCommand)); + assertTrue("Dynamic command for OS " + os + " is incorrect", Arrays.deepEquals(dynamicCommandCheck, dynamicCommand)); + } + + public void testNoTimeout() throws Exception + { + long timeout = -1; + int runFor = 10000; + + longishRunningProcess(runFor, timeout); + } + + public void testTimeout() throws Exception + { + long timeout = 5000; + int runFor = 10000; + + longishRunningProcess(runFor, timeout); + } + + private void longishRunningProcess(int runFor, long timeout) + { + long marginOfError = 3000; + boolean shouldComplete = timeout <= 0; + + assertTrue("The timeout when set must be more than "+marginOfError+"ms", shouldComplete || timeout >= marginOfError); + assertTrue("The timeout when set plus "+marginOfError+"ms must less than the runFor value", shouldComplete || timeout+marginOfError <= runFor); + + long minTime = (shouldComplete ? runFor : timeout) - marginOfError; + long maxTime = (shouldComplete ? runFor : timeout) + marginOfError; + + RuntimeExec exec = new RuntimeExec(); + + // This test will return different results on Windows and Linux! + // note that some Unix variants will error without a path + HashMap commandMap = new HashMap(); + commandMap.put("*", new String[] {"sleep", ""+(runFor/1000)}); + commandMap.put("Windows.*", new String[] {"ping", "-n", ""+(runFor/1000+1), "127.0.0.1"}); // don't you just love Microsoft + + // execute + exec.setCommandsAndArguments(commandMap); + long time = System.currentTimeMillis(); + ExecutionResult ret = exec.execute(Collections.emptyMap(), timeout); + time = System.currentTimeMillis()-time; + + String out = ret.getStdOut(); + String err = ret.getStdErr(); + + assertTrue("Command was too fast "+time+"ms", time >= minTime); + assertTrue("Command was too slow "+time+"ms", time <= maxTime); + + if (shouldComplete) + assertEquals("Didn't expect error code", 0, ret.getExitValue()); + else + assertFalse("Didn't expect success code", 0 == ret.getExitValue()); + } +} diff --git a/src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java b/src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java new file mode 100644 index 0000000000..47d4c56a8d --- /dev/null +++ b/src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.random; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @see NormalDistributionHelper + * + * @author Derek Hulley + * @since 5.1 + */ +public class NormalDistributionHelperTest +{ + private NormalDistributionHelper normalDistribution = new NormalDistributionHelper(); + + @Test + public void testGetValue_Fail() + { + try + { + normalDistribution.getValue(5L, -5L); + fail("Min-max relation not detected."); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + @Test + public void testGetValue_Precise() + { + assertEquals(10L, normalDistribution.getValue(10L, 10L)); + assertEquals(0L, normalDistribution.getValue(0L, 0L)); + assertEquals(-10L, normalDistribution.getValue(-10L, -10L)); + } + + @Test + public void testGetValue_Repeated() + { + for (int i = 0; i < 1000; i++) + { + long value = normalDistribution.getValue(-1*i, i); + assertTrue("Min not respected", value >= -1*i); + assertTrue("Max not respected", value <= i); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java b/src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java new file mode 100644 index 0000000000..1fa11f6b97 --- /dev/null +++ b/src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.resource; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.AbstractCollection; +import java.util.AbstractList; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.TreeSet; + +import junit.framework.TestCase; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.springframework.core.io.Resource; + +/** + * @see HierarchicalResourceLoader + * + * @author Derek Hulley + * @since 3.2 (Mobile) + */ +public class HierarchicalResourceLoaderTest extends TestCase +{ + private HierarchicalResourceLoader getLoader( + Class baseClazz, + Class clazz) throws Throwable + { + HierarchicalResourceLoader loader = new HierarchicalResourceLoader(); + loader.setDialectBaseClass(baseClazz.getName()); + loader.setDialectClass(clazz.getName()); + loader.afterPropertiesSet(); + return loader; + } + + /** + * Check that unmatched hierarchies are detected + */ + public void testMismatchDetection() throws Throwable + { + // First, do a successful few + getLoader(AbstractCollection.class, TreeSet.class); + getLoader(AbstractCollection.class, HashSet.class); + getLoader(AbstractCollection.class, ArrayList.class); + getLoader(AbstractCollection.class, AbstractCollection.class); + // Now blow up a bit + try + { + getLoader(Collection.class, Object.class).getResource("abc"); + fail("Failed to detect incompatible class hierarchy"); + } + catch (RuntimeException e) + { + // Expected + } + try + { + getLoader(ArrayList.class, AbstractCollection.class).getResource("abc"); + fail("Failed to detect incompatible class hierarchy"); + } + catch (RuntimeException e) + { + // Expected + } + } + + private void checkResource(Resource resource, String check) throws Throwable + { + assertNotNull("Resource not found", resource); + assertTrue("Resource doesn't exist", resource.exists()); + InputStream is = resource.getInputStream(); + StringBuilder builder = new StringBuilder(128); + byte[] bytes = new byte[128]; + InputStream tempIs = null; + try + { + tempIs = new BufferedInputStream(is, 128); + int count = -2; + while (count != -1) + { + // do we have something previously read? + if (count > 0) + { + String toWrite = new String(bytes, 0, count, "UTF-8"); + builder.append(toWrite); + } + // read the next set of bytes + count = tempIs.read(bytes); + } + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to read stream", e); + } + finally + { + // close the input stream + try + { + is.close(); + } + catch (Exception e) + { + } + } + // The string + String fileValue = builder.toString(); + assertEquals("Incorrect file retrieved: ", check, fileValue); + } + + private static final String RESOURCE = "classpath:resource-loader/#resource.dialect#/file.txt"; + /** + * Check that resource loading works. + * + * The data available is: + *
+     * classpatch:resource-loader/
+     *    java.util.AbstractCollection
+     *    java.util.AbstractList
+     *    java.util.TreeSet 
+     * 
+ * With each folder containing a text file with the name of the folder. + */ + public void testResourceLoading() throws Throwable + { + // First, do a successful few + HierarchicalResourceLoader bean; + Resource resource; + + bean = getLoader(AbstractCollection.class, TreeSet.class); + resource = bean.getResource(RESOURCE); + checkResource(resource, "java.util.TreeSet"); + + bean = getLoader(AbstractCollection.class, AbstractSet.class); + resource = bean.getResource(RESOURCE); + checkResource(resource, "java.util.AbstractCollection"); + + bean = getLoader(AbstractCollection.class, AbstractCollection.class); + resource = bean.getResource(RESOURCE); + checkResource(resource, "java.util.AbstractCollection"); + + bean = getLoader(AbstractCollection.class, ArrayList.class); + resource = bean.getResource(RESOURCE); + checkResource(resource, "java.util.AbstractList"); + + bean = getLoader(AbstractCollection.class, AbstractList.class); + resource = bean.getResource(RESOURCE); + checkResource(resource, "java.util.AbstractList"); + } +} diff --git a/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java b/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java new file mode 100644 index 0000000000..937c647225 --- /dev/null +++ b/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.shard; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.junit.Test; + +/** + * @author Andy + */ +public class ExplicitShardingPolicyTest +{ + + @Test + public void tenShards_noReplication_oneNodes() + { + ExplicitShardingPolicy policy = new ExplicitShardingPolicy(10, 1, 1); + assertTrue(policy.configurationIsValid()); + List shardIds = policy.getShardIdsForNode(1); + assertEquals(10, shardIds.size()); + for (int i = 0; i < 10; i++) + { + assertTrue(shardIds.contains(i)); + } + assertEquals(0, policy.getShardIdsForNode(2).size()); + + for (int i = 0; i < 10; i++) + { + List nodeInstances = policy.getNodeInstancesForShardId(i); + assertEquals(1, nodeInstances.size()); + } + } + + @Test + public void tenShards_noReplication_tenNodes() + { + ExplicitShardingPolicy policy = new ExplicitShardingPolicy(10, 1, 10); + assertTrue(policy.configurationIsValid()); + + for (int i = 0; i < 10; i++) + { + List shardIds = policy.getShardIdsForNode(i + 1); + assertEquals(1, shardIds.size()); + assertTrue(shardIds.contains(i)); + } + assertEquals(0, policy.getShardIdsForNode(11).size()); + + for (int i = 0; i < 10; i++) + { + List nodeInstances = policy.getNodeInstancesForShardId(i); + assertEquals(1, nodeInstances.size()); + + } + } + + @Test + public void tenShards_doubled_tenNodes() + { + ExplicitShardingPolicy policy = new ExplicitShardingPolicy(10, 2, 10); + assertTrue(policy.configurationIsValid()); + + for (int i = 0; i < 10; i++) + { + List shardIds = policy.getShardIdsForNode(i + 1); + assertEquals(2, shardIds.size()); + assertTrue(shardIds.contains(i)); + assertTrue(shardIds.contains((i + 1) % 10)); + } + assertEquals(0, policy.getShardIdsForNode(11).size()); + + for (int i = 0; i < 10; i++) + { + List nodeInstances = policy.getNodeInstancesForShardId(i); + assertEquals(2, nodeInstances.size()); + + } + } + + @Test + public void check_24_3() + { + buildAndTest(24, 3, 72); + buildAndTest(24, 3, 36); + buildAndTest(24, 3, 24); + buildAndTest(24, 3, 18); + buildAndTest(24, 3, 12); + buildAndTest(24, 3, 9); + buildAndTest(24, 3, 8); + buildAndTest(24, 3, 6); + buildAndTest(24, 3, 4); + buildAndTest(24, 3, 3); + } + + @Test + + public void failing() + { + buildAndTest(10, 2, 4); + } + + @Test + public void check_10_2() + { + buildAndTest(10, 2, 20); + buildAndTest(10, 2, 10); + buildAndTest(10, 2, 5); + buildAndTest(10, 2, 4); + buildAndTest(10, 2, 2); + } + + @Test + public void check_12_2() + { + buildAndTest(12, 2, 24); + buildAndTest(12, 2, 12); + buildAndTest(12, 2, 8); + buildAndTest(12, 2, 6); + buildAndTest(12, 2, 4); + buildAndTest(12, 2, 3); + buildAndTest(12, 2, 2); + } + + @Test + public void invalidConfiguration_nodes() + { + ExplicitShardingPolicy policy = new ExplicitShardingPolicy(10, 2, 11); + assertFalse(policy.configurationIsValid()); + + policy = new ExplicitShardingPolicy(10, 0, 10); + assertFalse(policy.configurationIsValid()); + + policy = new ExplicitShardingPolicy(0, 2, 10); + assertFalse(policy.configurationIsValid()); + + policy = new ExplicitShardingPolicy(10, 11, 10); + assertFalse(policy.configurationIsValid()); + } + + private void buildAndTest(int numShards, int replicationFactor, int numNodes) + { + ExplicitShardingPolicy policy = new ExplicitShardingPolicy(numShards, replicationFactor, numNodes); + assertTrue(policy.configurationIsValid()); + + int[] found = new int[numShards]; + for (int i = 0; i < numNodes; i++) + { + List shardIds = policy.getShardIdsForNode(i + 1); + assertEquals(numShards * replicationFactor / numNodes, shardIds.size()); + for (Integer shardId : shardIds) + { + found[shardId]++; + } + } + check(found, replicationFactor); + assertEquals(0, policy.getShardIdsForNode(numNodes + 1).size()); + + + int[] nodes = new int[numNodes]; + for(int i = 0; i < numShards; i++) + { + List nodeInstances = policy.getNodeInstancesForShardId(i); + assertEquals(replicationFactor, nodeInstances.size()); + for (Integer nodeInstance : nodeInstances) + { + nodes[nodeInstance-1]++; + } + } + check(nodes, numShards * replicationFactor / numNodes); + } + + /** + * @param found + * @param i + */ + private void check(int[] found, int count) + { + for (int i = 0; i < found.length; i++) + { + assertEquals(count, found[i]); + } + } +} diff --git a/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java b/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java new file mode 100644 index 0000000000..5c218bbafe --- /dev/null +++ b/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.transaction; + +import java.util.NoSuchElementException; + +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.springframework.transaction.CannotCreateTransactionException; +import org.springframework.transaction.NoTransactionException; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.interceptor.TransactionAspectSupport; +import org.springframework.transaction.support.AbstractPlatformTransactionManager; +import org.springframework.transaction.support.DefaultTransactionStatus; + +/** + * @see org.alfresco.util.transaction.SpringAwareUserTransaction + * + * @author Derek Hulley + */ +public class SpringAwareUserTransactionTest extends TestCase +{ + private DummyTransactionManager transactionManager; + private FailingTransactionManager failingTransactionManager; + private UserTransaction txn; + + public SpringAwareUserTransactionTest() + { + super(); + } + + @Override + protected void setUp() throws Exception + { + transactionManager = new DummyTransactionManager(); + failingTransactionManager = new FailingTransactionManager(); + txn = getTxn(); + } + + private UserTransaction getTxn() + { + return new SpringAwareUserTransaction( + transactionManager, + false, + TransactionDefinition.ISOLATION_DEFAULT, + TransactionDefinition.PROPAGATION_REQUIRED, + TransactionDefinition.TIMEOUT_DEFAULT); + } + + public void testSetUp() throws Exception + { + assertNotNull(transactionManager); + assertNotNull(txn); + } + + private void checkNoStatusOnThread() + { + try + { + TransactionAspectSupport.currentTransactionStatus(); + fail("Spring transaction info is present outside of transaction boundaries"); + } + catch (NoTransactionException e) + { + // expected + } + } + + public void testNoTxnStatus() throws Exception + { + checkNoStatusOnThread(); + assertEquals("Transaction status is not correct", + Status.STATUS_NO_TRANSACTION, + txn.getStatus()); + assertEquals("Transaction manager not set up correctly", + txn.getStatus(), + transactionManager.getStatus()); + } + + public void testSimpleTxnWithCommit() throws Throwable + { + testNoTxnStatus(); + try + { + txn.begin(); + assertEquals("Transaction status is not correct", + Status.STATUS_ACTIVE, + txn.getStatus()); + assertEquals("Transaction manager not called correctly", + txn.getStatus(), + transactionManager.getStatus()); + + txn.commit(); + assertEquals("Transaction status is not correct", + Status.STATUS_COMMITTED, + txn.getStatus()); + assertEquals("Transaction manager not called correctly", + txn.getStatus(), + transactionManager.getStatus()); + } + catch (Throwable e) + { + // unexpected exception - attempt a cleanup + try + { + txn.rollback(); + } + catch (Throwable ee) + { + e.printStackTrace(); + } + throw e; + } + checkNoStatusOnThread(); + } + + public void testSimpleTxnWithRollback() throws Exception + { + testNoTxnStatus(); + try + { + txn.begin(); + + throw new Exception("Blah"); + } + catch (Throwable e) + { + txn.rollback(); + } + assertEquals("Transaction status is not correct", + Status.STATUS_ROLLEDBACK, + txn.getStatus()); + assertEquals("Transaction manager not called correctly", + txn.getStatus(), + transactionManager.getStatus()); + checkNoStatusOnThread(); + } + + public void testNoBeginCommit() throws Exception + { + testNoTxnStatus(); + try + { + txn.commit(); + fail("Failed to detected no begin"); + } + catch (IllegalStateException e) + { + // expected + } + checkNoStatusOnThread(); + } + + public void testPostRollbackCommitDetection() throws Exception + { + testNoTxnStatus(); + + txn.begin(); + txn.rollback(); + try + { + txn.commit(); + fail("Failed to detect rolled back txn"); + } + catch (RollbackException e) + { + // expected + } + checkNoStatusOnThread(); + } + + public void testPostSetRollbackOnlyCommitDetection() throws Exception + { + testNoTxnStatus(); + + txn.begin(); + txn.setRollbackOnly(); + try + { + txn.commit(); + fail("Failed to detect set rollback"); + } + catch (RollbackException e) + { + // expected + txn.rollback(); + } + checkNoStatusOnThread(); + } + + public void testMismatchedBeginCommit() throws Exception + { + UserTransaction txn1 = getTxn(); + UserTransaction txn2 = getTxn(); + + testNoTxnStatus(); + + txn1.begin(); + txn2.begin(); + + txn2.commit(); + txn1.commit(); + + checkNoStatusOnThread(); + + txn1 = getTxn(); + txn2 = getTxn(); + + txn1.begin(); + txn2.begin(); + + try + { + txn1.commit(); + fail("Failure to detect mismatched transaction begin/commit"); + } + catch (RuntimeException e) + { + // expected + } + txn2.commit(); + txn1.commit(); + + checkNoStatusOnThread(); + } + + /** + * Test for leaked transactions (no guarantee it will succeed due to reliance + * on garbage collector), so disabled by default. + * + * Also, if it succeeds, transaction call stack tracing will be enabled + * potentially hitting the performance of all subsequent tests. + * + * @throws Exception + */ + public void xtestLeakedTransactionLogging() throws Exception + { + assertFalse(SpringAwareUserTransaction.isCallStackTraced()); + + TrxThread t1 = new TrxThread(); + t1.start(); + System.gc(); + Thread.sleep(1000); + + TrxThread t2 = new TrxThread(); + t2.start(); + System.gc(); + Thread.sleep(1000); + + assertTrue(SpringAwareUserTransaction.isCallStackTraced()); + + TrxThread t3 = new TrxThread(); + t3.start(); + System.gc(); + Thread.sleep(3000); + System.gc(); + Thread.sleep(3000); + } + + private class TrxThread extends Thread + { + public void run() + { + try + { + getTrx(); + } + catch (Exception e) {} + } + + public void getTrx() throws Exception + { + UserTransaction txn = getTxn(); + txn.begin(); + txn = null; + } + } + + public void testConnectionPoolException() throws Exception + { + testNoTxnStatus(); + txn = getFailingTxn(); + try + { + txn.begin(); + fail("ConnectionPoolException should be thrown."); + } + catch (ConnectionPoolException cpe) + { + // Expected fail + } + } + + private UserTransaction getFailingTxn() + { + return new SpringAwareUserTransaction( + failingTransactionManager, + false, + TransactionDefinition.ISOLATION_DEFAULT, + TransactionDefinition.PROPAGATION_REQUIRED, + TransactionDefinition.TIMEOUT_DEFAULT); + } + + /** + * Used to check that the transaction manager is being called correctly + * + * @author Derek Hulley + */ + @SuppressWarnings("serial") + private static class DummyTransactionManager extends AbstractPlatformTransactionManager + { + private int status = Status.STATUS_NO_TRANSACTION; + private Object txn = new Object(); + + /** + * @return Returns one of the {@link Status Status.STATUS_XXX} constants + */ + public int getStatus() + { + return status; + } + + protected void doBegin(Object arg0, TransactionDefinition arg1) + { + status = Status.STATUS_ACTIVE; + } + + protected void doCommit(DefaultTransactionStatus arg0) + { + status = Status.STATUS_COMMITTED; + } + + protected Object doGetTransaction() + { + return txn; + } + + protected void doRollback(DefaultTransactionStatus arg0) + { + status = Status.STATUS_ROLLEDBACK; + } + } + + /** + * Throws {@link NoSuchElementException} on begin() + * + * @author alex.mukha + */ + private static class FailingTransactionManager extends AbstractPlatformTransactionManager + { + private static final long serialVersionUID = 1L; + private int status = Status.STATUS_NO_TRANSACTION; + private Object txn = new Object(); + + /** + * @return Returns one of the {@link Status Status.STATUS_XXX} constants + */ + @SuppressWarnings("unused") + public int getStatus() + { + return status; + } + + protected void doBegin(Object arg0, TransactionDefinition arg1) + { + throw new CannotCreateTransactionException("Test exception."); + } + + protected void doCommit(DefaultTransactionStatus arg0) + { + status = Status.STATUS_COMMITTED; + } + + protected Object doGetTransaction() + { + return txn; + } + + protected void doRollback(DefaultTransactionStatus arg0) + { + status = Status.STATUS_ROLLEDBACK; + } + } +} diff --git a/src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml b/src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml new file mode 100644 index 0000000000..3cf31df419 --- /dev/null +++ b/src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml @@ -0,0 +1,44 @@ + + + + + + + + false + + + SYSTEM_PROPERTIES_MODE_OVERRIDE + + + false + + + + + + test.someString.#bean.dialect# + + + java.lang.String + + + java.util.AbstractCollection + + + ${hierarchy-test.dialect} + + + + + + + + + + + + + + diff --git a/src/test/resources/config-areas.xml b/src/test/resources/config-areas.xml new file mode 100644 index 0000000000..594902261c --- /dev/null +++ b/src/test/resources/config-areas.xml @@ -0,0 +1,13 @@ + + + + + value + + + + + A value + + + \ No newline at end of file diff --git a/src/test/resources/config-multi.xml b/src/test/resources/config-multi.xml new file mode 100644 index 0000000000..36b3116dde --- /dev/null +++ b/src/test/resources/config-multi.xml @@ -0,0 +1,30 @@ + + + + + + + + + + Another global value + true + + child two value + + + + + Another value + + + + the overridden first value + second value + + child two value + child three value + + + + \ No newline at end of file diff --git a/src/test/resources/config-props.properties b/src/test/resources/config-props.properties new file mode 100644 index 0000000000..fd5e95097e --- /dev/null +++ b/src/test/resources/config-props.properties @@ -0,0 +1,5 @@ +globalValue=globalValue +childOneValue=childOneValue +theValue=theValue +theAttr=attrValue +true=true diff --git a/src/test/resources/config-props.xml b/src/test/resources/config-props.xml new file mode 100644 index 0000000000..d975b5ce0d --- /dev/null +++ b/src/test/resources/config-props.xml @@ -0,0 +1,19 @@ + + + + The global value + ${globalValue} + ${missingGlobalValue} + false + + + + The value + ${theValue} + ${missingTheValue} + true + ${true} + ${missingTrue} + + + \ No newline at end of file diff --git a/src/test/resources/config-replace.xml b/src/test/resources/config-replace.xml new file mode 100644 index 0000000000..c95276c335 --- /dev/null +++ b/src/test/resources/config-replace.xml @@ -0,0 +1,19 @@ + + + + The replaced global value + + child custom value + + + + + the replaced first value + new fourth value + + child two value + child three value + + + + \ No newline at end of file diff --git a/src/test/resources/config.xml b/src/test/resources/config.xml new file mode 100644 index 0000000000..116b828479 --- /dev/null +++ b/src/test/resources/config.xml @@ -0,0 +1,57 @@ + + + + The global value + false + + child one value + + + child one value + child two value + child three value + + + + + The value + true + + + + first value + second value + third value + + child one value + + + + + first value + second value + third value + + child one value + + + + + + child one value + child two value + + + grand child one value + grand child two value + + + child four value + + + + + A value + + + \ No newline at end of file diff --git a/src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml b/src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml new file mode 100644 index 0000000000..cd3ffda6a4 --- /dev/null +++ b/src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml b/src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml new file mode 100644 index 0000000000..34ab7f88f2 --- /dev/null +++ b/src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml @@ -0,0 +1,51 @@ + + + + + + + + false + + + SYSTEM_PROPERTIES_MODE_OVERRIDE + + + false + + + + + + java.util.AbstractCollection + + + ${hierarchy-test.dialect} + + + + + + + + + + classpath:ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml + + + + + + + + + + java:comp/env/jdbc/dataSource + + + + + + + diff --git a/src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml b/src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml new file mode 100644 index 0000000000..e1895e60bc --- /dev/null +++ b/src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml b/src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml new file mode 100644 index 0000000000..72698980ea --- /dev/null +++ b/src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml b/src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml new file mode 100644 index 0000000000..b15273e2d8 --- /dev/null +++ b/src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/org/alfresco/i18n/testMessages.properties b/src/test/resources/org/alfresco/i18n/testMessages.properties new file mode 100644 index 0000000000..6d54f72162 --- /dev/null +++ b/src/test/resources/org/alfresco/i18n/testMessages.properties @@ -0,0 +1,4 @@ +msg_yes=Yes +msg_no=No +msg_params=What no {0}? +msg_error=This is an error message. \n This is on a new line. \ No newline at end of file diff --git a/src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties b/src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties new file mode 100644 index 0000000000..7db02235fb --- /dev/null +++ b/src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties @@ -0,0 +1,4 @@ +msg_yes=Oui +msg_no=Non +msg_params=Que non {0}? +msg_error=C'est un message d'erreur. \n C'est sur une nouvelle ligne. \ No newline at end of file diff --git a/src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml b/src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml new file mode 100644 index 0000000000..e68189f6b1 --- /dev/null +++ b/src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + dir + + + + + ls + + + + + + + + value1 + + + null + + + ${env.prop3.unsubstituted} + + + + + . + + + true + + + 1, 2 + + + + + + + + + dir + SPLIT:${paths} + + + + + + + . .. + + + + 1, 2 + + + + + + + + + SPLIT: dir . .. + + + + + + 1, 2 + + + + + + + + + dir c: + + + ls / + + + + + true + + + 1 + + + + + + + + + cmd + /C + rmdir + ${dir} + + + + + rm + -rf + ${dir} + + + + + rm + -rf + ${dir} + + + + + wibble + + + + + + + dir RuntimeExecBootstrapBeanTest + + + + 1, 2 + + + + + + + + + + wibble + + + + + + 666 + + + + + + + + + + + + + + + + + + + + + + + + + + cmd + + + + + ls + + + + + + false + + + 1 + + + + + + + + + + dir + ${dir} + + + + + ls + ${dir} + + + + + + true + + + 1 + + + + diff --git a/src/test/resources/resource-loader/java.util.AbstractCollection/file.txt b/src/test/resources/resource-loader/java.util.AbstractCollection/file.txt new file mode 100644 index 0000000000..284f2bfe50 --- /dev/null +++ b/src/test/resources/resource-loader/java.util.AbstractCollection/file.txt @@ -0,0 +1 @@ +java.util.AbstractCollection \ No newline at end of file diff --git a/src/test/resources/resource-loader/java.util.AbstractList/file.txt b/src/test/resources/resource-loader/java.util.AbstractList/file.txt new file mode 100644 index 0000000000..f6ba039304 --- /dev/null +++ b/src/test/resources/resource-loader/java.util.AbstractList/file.txt @@ -0,0 +1 @@ +java.util.AbstractList \ No newline at end of file diff --git a/src/test/resources/resource-loader/java.util.TreeSet/file.txt b/src/test/resources/resource-loader/java.util.TreeSet/file.txt new file mode 100644 index 0000000000..8817feae36 --- /dev/null +++ b/src/test/resources/resource-loader/java.util.TreeSet/file.txt @@ -0,0 +1 @@ +java.util.TreeSet \ No newline at end of file diff --git a/src/test/resources/test-config-forms-basic-override.xml b/src/test/resources/test-config-forms-basic-override.xml new file mode 100644 index 0000000000..604074e48f --- /dev/null +++ b/src/test/resources/test-config-forms-basic-override.xml @@ -0,0 +1,99 @@ + + + + + + + 999 + + + Goodbye + + This is new + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + 500px + bar + + + + + + +
+
+
+ + + +
+ + + + + + + + + + + + + + + +
+
+ + + +
+ + + + + +
+
+
+ + + +
+ + + +
+
+
+ +
+ \ No newline at end of file diff --git a/src/test/resources/test-config-forms-basic.xml b/src/test/resources/test-config-forms-basic.xml new file mode 100644 index 0000000000..1127991ca9 --- /dev/null +++ b/src/test/resources/test-config-forms-basic.xml @@ -0,0 +1,172 @@ + + + + + + + + + + + + + 50 + + + Hello + + + + + + 1 + Hello + For ever and ever. + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + 500px + + + + + + + + 10 + 500px + + + + + + + +
+ +
+
+
+
+ + + +
+ + + + + + + + + + + + + +
+
+ + + +
+ + + + +
+
+
+ + + +
+ + + +
+
+
+ + + +
+ + + +
+
+
+ + + +
+ + + + + + + + +
+
+
+ + + +
+ + + + + + + + + +
+
+ + +
diff --git a/src/test/resources/test-config-forms-negative.xml b/src/test/resources/test-config-forms-negative.xml new file mode 100644 index 0000000000..35abe68e9f --- /dev/null +++ b/src/test/resources/test-config-forms-negative.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
\ No newline at end of file diff --git a/src/test/resources/test-config-forms.xml b/src/test/resources/test-config-forms.xml new file mode 100644 index 0000000000..6010638f2f --- /dev/null +++ b/src/test/resources/test-config-forms.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + 50 + + + + + 1 + Hello + Greetings + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + bar + + + + + + + + + + + + un + deux + + + quatre + + + + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + +
+ + + + + + + + + + + + + + + + +
+
+
+
\ No newline at end of file From 3458f7c30e456f9ae0ee55d058329f4915763c95 Mon Sep 17 00:00:00 2001 From: dhulley Date: Thu, 1 Sep 2016 20:28:07 +0100 Subject: [PATCH 003/282] Move 'scm' tags to git --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ca086f3f5d..a51e14df72 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,8 @@ Alfresco core libraries and utils - scm:svn:https://svn.alfresco.com/repos/alfresco-open-mirror/services/alfresco-core/trunk/ - scm:svn:https://svn.alfresco.com/repos/alfresco-enterprise/services/alfresco-core/trunk/ + scm:git:https://gitlab.alfresco.com/platform/alfresco-core + scm:git:https://gitlab.alfresco.com/platform/alfresco-core From b882039b878ff4b3857dc06414abe9ba8d039ceb Mon Sep 17 00:00:00 2001 From: Samuel Langlois Date: Fri, 2 Sep 2016 11:24:29 +0100 Subject: [PATCH 004/282] BDE-780 Fix format of git URL --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a51e14df72..a330f5676c 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,9 @@ Alfresco core libraries and utils - scm:git:https://gitlab.alfresco.com/platform/alfresco-core - scm:git:https://gitlab.alfresco.com/platform/alfresco-core + scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git + scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git + https://gitlab.alfresco.com/platform/alfresco-core From 28d4e462ff46a58ae6c1655d68c841ba6dc0dbef Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 2 Sep 2016 11:39:37 +0100 Subject: [PATCH 005/282] [maven-release-plugin] prepare release 6.5 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a330f5676c..1f70461743 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.5-SNAPSHOT + 6.5 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,8 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - + 6.5 + From 698c2cc4c740027ee82df3c55967f660fd56f367 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 2 Sep 2016 11:39:39 +0100 Subject: [PATCH 006/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1f70461743..c684edf789 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.5 + 6.6-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - 6.5 + HEAD From 88455358a7d081d9f640e903d5b3a5f47bb8086e Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 2 Sep 2016 14:26:32 +0100 Subject: [PATCH 007/282] [maven-release-plugin] prepare release 6.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c684edf789..1f70461743 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.6-SNAPSHOT + 6.5 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - HEAD + 6.5 From 44c359b9c70ca8d5b3a4216cd67f0ae58526cb0f Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 2 Sep 2016 14:26:34 +0100 Subject: [PATCH 008/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1f70461743..c684edf789 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.5 + 6.6-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - 6.5 + HEAD From c1f5343872509abbbaf728dcb21697fdf3d0ae55 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Mon, 19 Sep 2016 13:43:59 +0100 Subject: [PATCH 009/282] Fixed JavaDoc generation for JDK8 --- pom.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pom.xml b/pom.xml index c684edf789..19a32876a7 100644 --- a/pom.xml +++ b/pom.xml @@ -232,4 +232,24 @@ + + + doclint-java8-max + + [1.8,) + + + + + maven-javadoc-plugin + + + -Xdoclint:none + + + + + + + From ba5c37deac3b05676168e92f0a7c479e07c339da Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Thu, 13 Oct 2016 12:23:40 +0300 Subject: [PATCH 010/282] REPO-843 / REPO-1334: Upgrade to Servlet API 3.0.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 19a32876a7..d082b5e26d 100644 --- a/pom.xml +++ b/pom.xml @@ -166,8 +166,8 @@ javax.servlet - servlet-api - 2.5 + javax.servlet-api + 3.0.1 provided From 919cd45c1ad74bb4473265452985d9550bcd96a2 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 13 Oct 2016 17:50:34 +0100 Subject: [PATCH 011/282] [maven-release-plugin] prepare release 6.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d082b5e26d..865d97ea45 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.6-SNAPSHOT + 6.6 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - HEAD + 6.6 From 93719921ce194f16ab7166c1ba48295bf8449e43 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 13 Oct 2016 17:50:37 +0100 Subject: [PATCH 012/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 865d97ea45..f55d63cedf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.6 + 6.7-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - 6.6 + HEAD From 1288945c00a5a52c2a2e53086224e85ddab3bb0a Mon Sep 17 00:00:00 2001 From: Steven Glover Date: Tue, 1 Nov 2016 21:42:26 +0000 Subject: [PATCH 013/282] MNT-16541: "Cache update for custom workflow model fails" --- .../util/cache/AbstractAsynchronouslyRefreshedCache.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java b/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java index ce7012b112..0c6c076786 100644 --- a/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java +++ b/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.locks.ReentrantReadWriteLock; + import org.alfresco.util.PropertyCheck; import org.alfresco.util.transaction.TransactionListener; import org.alfresco.util.transaction.TransactionSupportUtil; @@ -201,6 +202,8 @@ public abstract class AbstractAsynchronouslyRefreshedCache { liveLock.writeLock().unlock(); } + + broadcastEvent(new RefreshableCacheRefreshedEvent(cacheId, key)); } protected void waitForBuild(Refresh refresh) From 5f999fca5b53422ed2a63b8cad4490a942abbc5c Mon Sep 17 00:00:00 2001 From: Steven Glover Date: Wed, 2 Nov 2016 09:58:41 +0000 Subject: [PATCH 014/282] MNT-16541: "Cache update for custom workflow model fails" create RC1 for testing --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f55d63cedf..8f2a8c15b5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.7-SNAPSHOT + REPO1259-6.7-RC1 Alfresco Core Alfresco core libraries and utils From 5fa5590b891ed3a2b6cb24dbd31498d5ce02e5e2 Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Wed, 2 Nov 2016 12:24:57 +0200 Subject: [PATCH 015/282] REPO-1238 / REPO-1413: Upgrade to OpenCMIS 1.0 - upgraded jaxb libraries to 2.2.11 (used by Apache CXF jars introduced by the new OpenCMIS) --- pom.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f55d63cedf..a4bc8e93ce 100644 --- a/pom.xml +++ b/pom.xml @@ -130,12 +130,17 @@ com.sun.xml.bind jaxb-xjc - 2.2.7 + 2.2.11 com.sun.xml.bind jaxb-impl - 2.2.7 + 2.2.11 + + + com.sun.xml.bind + jaxb-core + 2.2.11 dom4j From da7c903ea22ac3ea24e4f78d919877bf19cbfcf9 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 2 Nov 2016 10:30:13 +0000 Subject: [PATCH 016/282] [maven-release-plugin] prepare release 6.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a4bc8e93ce..71ef3e4bf5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.7-SNAPSHOT + 6.7 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - HEAD + 6.7 From 85bc1f5378d5efadcbadc78d306605852b97b05b Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 2 Nov 2016 10:30:16 +0000 Subject: [PATCH 017/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 71ef3e4bf5..d2f79400b2 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.7 + 6.8-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - 6.7 + HEAD From e5474b6d792d755a5d6e8d312c47884dc3d500e6 Mon Sep 17 00:00:00 2001 From: Steven Glover Date: Wed, 2 Nov 2016 12:56:35 +0000 Subject: [PATCH 018/282] Revert "MNT-16541: "Cache update for custom workflow model fails" create RC1 for testing" This reverts commit 5f999fca5b53422ed2a63b8cad4490a942abbc5c. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8f2a8c15b5..f55d63cedf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - REPO1259-6.7-RC1 + 6.7-SNAPSHOT Alfresco Core Alfresco core libraries and utils From 9ec4fb0df1f6e0d35240fe094bde32256ca2c216 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 2 Nov 2016 13:02:52 +0000 Subject: [PATCH 019/282] [maven-release-plugin] prepare release REPO1259-RC1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f55d63cedf..81fab344f3 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.7-SNAPSHOT + REPO1259-RC1 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - HEAD + REPO1259-RC1 From e14ea4249728c7d92b6a3077cf48a406c91d2a90 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 2 Nov 2016 13:02:55 +0000 Subject: [PATCH 020/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 81fab344f3..cf5c31aac0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - REPO1259-RC1 + -SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - REPO1259-RC1 + HEAD From 583cabfb3c963ac2b063b78b831974dc225a62c6 Mon Sep 17 00:00:00 2001 From: Steven Glover Date: Wed, 2 Nov 2016 16:16:06 +0000 Subject: [PATCH 021/282] MNT-16541: "Cache update for custom workflow model fails" fix up version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf5c31aac0..f55d63cedf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - -SNAPSHOT + 6.7-SNAPSHOT Alfresco Core Alfresco core libraries and utils From a0b201d095e13043ac4212754940a1b3a3538e50 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 3 Nov 2016 15:38:45 +0000 Subject: [PATCH 022/282] [maven-release-plugin] prepare release 6.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d2f79400b2..4f3331c27a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.8-SNAPSHOT + 6.8 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - HEAD + 6.8 From 33bc89e3f94b082e062b9cdd41a0a396c983e85d Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 3 Nov 2016 15:38:47 +0000 Subject: [PATCH 023/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4f3331c27a..5aaf940bb7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.8 + 6.9-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - 6.8 + HEAD From b7db8d994420dbef32002fcb138d70791516835a Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Fri, 25 Nov 2016 21:10:35 +0000 Subject: [PATCH 024/282] REPO-1489: Added logger config required to display WARNs --- src/main/resources/log4j.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index b4a8ca66bf..1938ee2783 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -149,6 +149,9 @@ log4j.logger.org.alfresco.fileserver=warn # Integrity message threshold - if 'failOnViolation' is off, then WARNINGS are generated log4j.logger.org.alfresco.repo.node.integrity=ERROR +# Brute force attack detection +log4j.logger.org.alfresco.repo.security.authentication.AuthenticationServiceImpl=warn + # Indexer debugging log4j.logger.org.alfresco.repo.search.Indexer=error #log4j.logger.org.alfresco.repo.search.Indexer=debug From 3d78b691bad981422f55ea656d517650c3325614 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 25 Nov 2016 21:14:39 +0000 Subject: [PATCH 025/282] [maven-release-plugin] prepare release 6.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5aaf940bb7..1c963b59ae 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.9-SNAPSHOT + 6.9 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - HEAD + 6.9 From 89a547a5f64fffecc2cd8df5bd73c26d77b110b9 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 25 Nov 2016 21:14:43 +0000 Subject: [PATCH 026/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1c963b59ae..9b22ad0518 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 6 alfresco-core - 6.9 + 6.10-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - 6.9 + HEAD From 14170f7ed74c90a7c135dd2f41c9aca305df0c2e Mon Sep 17 00:00:00 2001 From: dhulley Date: Sat, 26 Nov 2016 09:07:28 +0000 Subject: [PATCH 027/282] Use super pom V8, which includes a profile for the javadoc --- pom.xml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index 9b22ad0518..8eff9d121d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-super-pom - 6 + 8 alfresco-core 6.10-SNAPSHOT @@ -237,24 +237,4 @@ - - - doclint-java8-max - - [1.8,) - - - - - maven-javadoc-plugin - - - -Xdoclint:none - - - - - - - From 7b370bed4f6362bbdefc2b6fa0e60e8af8c84415 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Mon, 16 Jan 2017 10:40:02 +0000 Subject: [PATCH 028/282] MNT-17247: extended logging --- src/main/resources/log4j.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index 1938ee2783..af37bef7db 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -149,8 +149,9 @@ log4j.logger.org.alfresco.fileserver=warn # Integrity message threshold - if 'failOnViolation' is off, then WARNINGS are generated log4j.logger.org.alfresco.repo.node.integrity=ERROR -# Brute force attack detection -log4j.logger.org.alfresco.repo.security.authentication.AuthenticationServiceImpl=warn +# Authentication +# Specifically brute force attack detection +log4j.logger.org.alfresco.repo.security.authentication=warn # Indexer debugging log4j.logger.org.alfresco.repo.search.Indexer=error From 0c479464d69bd78c1634f0aad943520bf5e40397 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Mon, 16 Jan 2017 10:46:21 +0000 Subject: [PATCH 029/282] [maven-release-plugin] prepare release 6.10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8eff9d121d..cac165a410 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.10-SNAPSHOT + 6.10 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - HEAD + 6.10 From 84cd2d8736583a172a8991239957f42118583dd4 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Mon, 16 Jan 2017 10:46:23 +0000 Subject: [PATCH 030/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cac165a410..7e25e94585 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.10 + 6.11-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git https://gitlab.alfresco.com/platform/alfresco-core - 6.10 + HEAD From 55460bac4d42292e2b7ef746fe4416d82198c2f6 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Thu, 6 Apr 2017 11:30:08 +0100 Subject: [PATCH 031/282] Add readme and contributing guide. --- CONTRIBUTING.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 36 ++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 README.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..a56864a993 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,68 @@ +### Contributing +Thanks for taking the time to contribute! + +The following is a set of guidelines for contributing to this library. Most of them will make the life of the reviewer easier and therefore decrease the time required for the patch go to the next version of the library. + +Please, take a look at the contribution information in the [Community Site](https://community.alfresco.com/docs/DOC-6385-project-overview-repository) + +#### Reporting bugs +The bug can be submitted as an issue on GitHub. But the best way to report a bug is to create an issue in [JIRA tracker](https://issues.alfresco.com). Ideally supported by a good pull request. + +#### Suggesting enhancements +The enhancements can be submitted as an issue on GitHub. + +#### Pull requests +* Describe what is in the code and include the JIRA number of the reported bug if applicable. +* Follow the [Style guides](#style-guides) +* Add/modify the tests to check the new code. The test coverage should be enough to support all of the changes to prevent regressions in future. +Please, pay attention to the level of test being done. It is preferred to create unit tests as opposing to system/integration tests. Unit tests are simpler, easier to maintain and take less time to run. + +#### Style guides + +##### Code formatting +* Charset is UTF-8 +* Line endings are CRLF +* All braces are on new lines +* The braces are enforced everywhere even if not explicitly required +* The multi statement constructions like *try-catch-finally* have each statement on new line +* Wrap code at 255 characters +* Tabs are substituted with 4 spaces +* Empty lines do not have tabs/spaces +* All new public methods have JavaDoc +* The JavaDoc should be compliant with [Java8 DocLint](http://openjdk.java.net/jeps/172) +* If the code is not self explanatory, then comments/JavaDoc should be added as appropriate. Excessive comments should be avoided. +* The strings which are shown to the user (in the UI) should be put in localization property bundles. Our localization team will handle the translations if required. + +##### Commit message +* Separate subject from body with a blank line +* Limit the subject line to 50 characters +* Capitalize the subject line +* Do not end the subject line with a period +* Use the imperative mood in the subject line +* Include JIRA numbers in the subject line +* Wrap the body at 72 characters +* Use the body to explain what and why vs. how +* Use a hyphen as a bullet point in the lists + +Example: +~~~ +ALF-12345 Summarize changes in around 50 characters or less + +More detailed explanatory text, if necessary. Wrap it to about 72 +characters or so. In some contexts, the first line is treated as the +subject of the commit and the rest of the text as the body. The +blank line separating the summary from the body is critical (unless +you omit the body entirely); various tools like `log`, `shortlog` +and `rebase` can get confused if you run the two together. + +Explain the problem that this commit is solving. Focus on why you +are making this change as opposed to how (the code explains that). +Are there side effects or other unintuitive consequences of this +change? Here's the place to explain them. + +Further paragraphs come after blank lines. + + - Bullet points are okay, too + - Use a hyphen for the bullet, preceded + by a single space +~~~ diff --git a/README.md b/README.md new file mode 100644 index 0000000000..0092ca5deb --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +### Alfresco Core +Alfresco Core is a library packaged as a jar file which is part of [Alfresco Content Services Repository](https://community.alfresco.com/docs/DOC-6385-project-overview-repository). +The library contains the following: +* Various helpers and utils +* Canned queries interface and supporting classes +* Generic encryption supporting classes + + +### Building and testing +The project can be built and tested by running Maven command: +~~~ +mvn clean install +~~~ + +### Artifacts +The artifacts can be obtained by: +* downloading from [Alfresco repository](https://artifacts.alfresco.com/nexus/content/groups/public) +* getting as Maven dependency by adding the dependency to your pom file: +~~~ + + org.alfresco + alfresco-core + version + +~~~ +and Alfresco repository: +~~~ + + alfresco-maven-repo + https://artifacts.alfresco.com/nexus/content/groups/public + +~~~ +The SNAPSHOT version of the artifact is **never** published. + +### Contributing guide +Please use [this guide](CONTRIBUTING.md) to make a contribution to the project. \ No newline at end of file From ae9d595d8cb569e0725e3eab588e547c157250b0 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Thu, 6 Apr 2017 12:05:45 +0100 Subject: [PATCH 032/282] Add License --- LICENSE.txt | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..65c5ca88a6 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. From 53a9918fc86ff4b43a3cb8febca88e94d2692651 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Thu, 6 Apr 2017 12:33:55 +0100 Subject: [PATCH 033/282] Add a link to old version history in SVN --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0092ca5deb..8abfef4581 100644 --- a/README.md +++ b/README.md @@ -32,5 +32,8 @@ and Alfresco repository: ~~~ The SNAPSHOT version of the artifact is **never** published. +### Old version history +The history for older versions can be found in [Alfresco SVN](https://svn.alfresco.com/repos/alfresco-open-mirror/services/alfresco-core/) + ### Contributing guide Please use [this guide](CONTRIBUTING.md) to make a contribution to the project. \ No newline at end of file From 6b3003fa0f8c0053a358a8009f69507fda597b45 Mon Sep 17 00:00:00 2001 From: "NESS\\P3700654" Date: Fri, 26 May 2017 10:07:17 +0300 Subject: [PATCH 034/282] MNT-17640: Log4j is not configured for support tool Added jmxlogger1 to log4j.properties files, as they were ommited when support tools was integrated in CS 5.2 --- src/main/resources/log4j.properties | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index af37bef7db..30e74fd3c5 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -1,5 +1,5 @@ # Set root logger level to error -log4j.rootLogger=error, Console, File +log4j.rootLogger=error, Console, File, jmxlogger1 ###### Console appender definition ####### @@ -19,6 +19,14 @@ log4j.appender.File.DatePattern='.'yyyy-MM-dd log4j.appender.File.layout=org.apache.log4j.PatternLayout log4j.appender.File.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} %-5p [%c] [%t] %m%n +###### JmxLogger appender definition ####### +log4j.appender.jmxlogger1=jmxlogger.integration.log4j.JmxLogAppender +log4j.appender.jmxlogger1.layout=org.apache.log4j.PatternLayout +log4j.appender.jmxlogger1.layout.ConversionPattern=%-5p %c[1] - %m%n +log4j.appender.jmxlogger1.ObjectName=jmxlogger:type=LogEmitterAlfresco +log4j.appender.jmxlogger1.threshold=debug +log4j.appender.jmxlogger1.serverSelection=platform + ###### Hibernate specific appender definition ####### #log4j.appender.file=org.apache.log4j.FileAppender #log4j.appender.file.File=hibernate.log From 571fbcc2714b7b9625abfe66fa10f765bfa3a4aa Mon Sep 17 00:00:00 2001 From: "NESS\\P3700654" Date: Fri, 26 May 2017 14:14:11 +0300 Subject: [PATCH 035/282] Fixed the pom.xml (Changed connection/devConnection/url from gitlab to github) in order for bamboo to push commits, it`s needed by the release stage. --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 7e25e94585..4a274a16b5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,9 +12,9 @@ Alfresco core libraries and utils - scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git - scm:git:https://gitlab.alfresco.com/platform/alfresco-core.git - https://gitlab.alfresco.com/platform/alfresco-core + scm:git:https://github.com/Alfresco/alfresco-core.git + scm:git:https://github.com/Alfresco/alfresco-core.git + https://github.com/Alfresco/alfresco-core HEAD From 2cbce0118cb9a7e16a1598aa6731103ed4ad6e0c Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 26 May 2017 11:18:37 +0000 Subject: [PATCH 036/282] [maven-release-plugin] prepare release 6.11 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4a274a16b5..c3467a420b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.11-SNAPSHOT + 6.11 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.11 From a40368b99c2585f12b010de2ee6b2fd08e5ad5eb Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 26 May 2017 11:18:42 +0000 Subject: [PATCH 037/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c3467a420b..1f93d70c6a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.11 + 6.12-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.11 + HEAD From 391d5bb29ece52d7dc0c1c51bbc9dd8c4e595562 Mon Sep 17 00:00:00 2001 From: Erik Knizat Date: Wed, 14 Jun 2017 10:48:19 +0100 Subject: [PATCH 038/282] REPO-2448 MNT-17613 Introduce support for overriding the dialect name in configuration --- .../hibernate/DialectFactoryBean.java | 39 ++++++++++- .../hibernate/DialectFactoryBeanTest.java | 66 +++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java diff --git a/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java b/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java index 75f6b5f25a..92cbe6a418 100644 --- a/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java +++ b/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java @@ -21,6 +21,8 @@ package org.alfresco.hibernate; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -42,6 +44,24 @@ public class DialectFactoryBean implements FactoryBean /** The local session factory. */ private LocalSessionFactoryBean localSessionFactory; + /** The map used in mapping driver name to a dialect. **/ + private Map driverDialectMap; + + + public void setDriverDialectMap(Map driverDialectMap) + { + this.driverDialectMap = driverDialectMap; + + } + + public Map getDriverDialectMap() + { + return this.driverDialectMap; + } + + + + /** * Sets the local session factory. * @@ -66,8 +86,11 @@ public class DialectFactoryBean implements FactoryBean con = session.connection(); con.setAutoCommit(true); DatabaseMetaData meta = con.getMetaData(); - Dialect dialect = DialectFactory.buildDialect(cfg.getProperties(), meta.getDatabaseProductName(), meta - .getDatabaseMajorVersion()); + + overrideDialectPropertyForDriver(cfg.getProperties(), meta.getDriverName()); + + Dialect dialect = DialectFactory.buildDialect(cfg.getProperties(), meta.getDatabaseProductName(), meta.getDatabaseMajorVersion()); + dialect = changeDialect(cfg, dialect); return dialect; } @@ -83,6 +106,18 @@ public class DialectFactoryBean implements FactoryBean } } + + /** + * Override or add a dialect property to props based on the driver name + */ + void overrideDialectPropertyForDriver(Properties props, String driverName) + { + if (driverDialectMap != null && driverDialectMap.containsKey(driverName)) + { + props.setProperty(Environment.DIALECT, driverDialectMap.get(driverName)); + } + } + /** * Substitute the dialect with an alternative, if possible. * diff --git a/src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java b/src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java new file mode 100644 index 0000000000..09a0e5be8d --- /dev/null +++ b/src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.hibernate; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.hibernate.cfg.Environment; +import org.junit.Test; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + + +/** + * @author Erik Knizat + */ +public class DialectFactoryBeanTest +{ + private static final String MARIADB_DIALECT_NAME = "org.alfresco.repo.domain.hibernate.dialect.AlfrescoMariaDBDialect"; + private static final String MARIA_DB_DRIVER_NAME = "MariaDB connector/J"; + + @Test + public void testMariaDBDialectGetsAdded() + { + DialectFactoryBean dfb = new DialectFactoryBean(); + Map driverDialectMap = new HashMap<>(); + driverDialectMap.put(MARIA_DB_DRIVER_NAME, MARIADB_DIALECT_NAME); + dfb.setDriverDialectMap(driverDialectMap); + Properties props = new Properties(); + dfb.overrideDialectPropertyForDriver(props, MARIA_DB_DRIVER_NAME); + + assertNotNull("The dialect property was not set for the driver.", props.getProperty((Environment.DIALECT))); + assertEquals("Dialect name did not match.", MARIADB_DIALECT_NAME, props.getProperty((Environment.DIALECT))); + } + + @Test + public void testDialectNotAddedIfNotSpecifiedForDriver() + { + DialectFactoryBean dfb = new DialectFactoryBean(); + Map driverDialectMap = new HashMap<>(); + dfb.setDriverDialectMap(driverDialectMap); // Add empty dialect driver map + Properties props = new Properties(); + dfb.overrideDialectPropertyForDriver(props, MARIA_DB_DRIVER_NAME); + + assertNull("Dialect name property was set for unspecified driver name.", props.getProperty((Environment.DIALECT))); + } + +} From 42da885b3d16c8bc5ffb6158c2f68d8f27d8b738 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 14 Jun 2017 10:28:56 +0000 Subject: [PATCH 039/282] [maven-release-plugin] prepare release 6.12 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1f93d70c6a..f63a0a896e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.12-SNAPSHOT + 6.12 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.12 From 972601bfeb06ac289c739a7ffd31a59300c2a181 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 14 Jun 2017 10:29:01 +0000 Subject: [PATCH 040/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f63a0a896e..1203af1fe1 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.12 + 6.13-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.12 + HEAD From 733d6db584df85dc388029a212c31ed71ab548e3 Mon Sep 17 00:00:00 2001 From: Martin Muller Date: Thu, 29 Jun 2017 10:46:39 +0100 Subject: [PATCH 041/282] Revert "MNT-17640: Log4j is not configured for support tool" This reverts commit 6b3003fa0f8c0053a358a8009f69507fda597b45. --- src/main/resources/log4j.properties | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index 30e74fd3c5..af37bef7db 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -1,5 +1,5 @@ # Set root logger level to error -log4j.rootLogger=error, Console, File, jmxlogger1 +log4j.rootLogger=error, Console, File ###### Console appender definition ####### @@ -19,14 +19,6 @@ log4j.appender.File.DatePattern='.'yyyy-MM-dd log4j.appender.File.layout=org.apache.log4j.PatternLayout log4j.appender.File.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} %-5p [%c] [%t] %m%n -###### JmxLogger appender definition ####### -log4j.appender.jmxlogger1=jmxlogger.integration.log4j.JmxLogAppender -log4j.appender.jmxlogger1.layout=org.apache.log4j.PatternLayout -log4j.appender.jmxlogger1.layout.ConversionPattern=%-5p %c[1] - %m%n -log4j.appender.jmxlogger1.ObjectName=jmxlogger:type=LogEmitterAlfresco -log4j.appender.jmxlogger1.threshold=debug -log4j.appender.jmxlogger1.serverSelection=platform - ###### Hibernate specific appender definition ####### #log4j.appender.file=org.apache.log4j.FileAppender #log4j.appender.file.File=hibernate.log From f64ce7abf5e6fe2cbcc3b52a24f7444b515238bb Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 29 Jun 2017 09:53:48 +0000 Subject: [PATCH 042/282] [maven-release-plugin] prepare release 6.13 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1203af1fe1..ccda0ff177 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.13-SNAPSHOT + 6.13 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.13 From 655293d1637fe962d8b6b983727e6372b5858607 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 29 Jun 2017 09:53:53 +0000 Subject: [PATCH 043/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ccda0ff177..86fe606b74 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.13 + 6.14-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.13 + HEAD From 422cc4773e1230c05b654f0c1b852f9adb011d0a Mon Sep 17 00:00:00 2001 From: Richard Esplin Date: Mon, 7 Aug 2017 19:44:28 -0600 Subject: [PATCH 044/282] Updated to leverage the material already existing I moved most of the content of this guide to the related pages in the Alfresco Social Community. --- CONTRIBUTING.md | 72 +++++++++---------------------------------------- 1 file changed, 12 insertions(+), 60 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a56864a993..d8b80182d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,68 +1,20 @@ ### Contributing -Thanks for taking the time to contribute! +Thanks for your interest in contributing to this project! -The following is a set of guidelines for contributing to this library. Most of them will make the life of the reviewer easier and therefore decrease the time required for the patch go to the next version of the library. +The following is a set of guidelines for contributing to this library. Most of them will make the life of the reviewer easier and therefore decrease the time required for the patch be included in the next version. -Please, take a look at the contribution information in the [Community Site](https://community.alfresco.com/docs/DOC-6385-project-overview-repository) +Because this project forms a part of Alfresco Content Services, the guidelines are hosted in the [Alfresco Social Community](http://community.alfresco.com/community/ecm) where they can be referenced from multiple projects. -#### Reporting bugs -The bug can be submitted as an issue on GitHub. But the best way to report a bug is to create an issue in [JIRA tracker](https://issues.alfresco.com). Ideally supported by a good pull request. +An overview on how this project is goverened is here: +https://community.alfresco.com/docs/DOC-6385-project-overview-repository -#### Suggesting enhancements -The enhancements can be submitted as an issue on GitHub. +You can report an issue in the ALF project of the [Alfresco issue tracker](http://issues.alfresco.com). -#### Pull requests -* Describe what is in the code and include the JIRA number of the reported bug if applicable. -* Follow the [Style guides](#style-guides) -* Add/modify the tests to check the new code. The test coverage should be enough to support all of the changes to prevent regressions in future. -Please, pay attention to the level of test being done. It is preferred to create unit tests as opposing to system/integration tests. Unit tests are simpler, easier to maintain and take less time to run. +Instructions for a good issue report are here: +https://community.alfresco.com/docs/DOC-6263-reporting-an-issue -#### Style guides +Instructions for making a contribution are here: +https://community.alfresco.com/docs/DOC-6269-submitting-contributions -##### Code formatting -* Charset is UTF-8 -* Line endings are CRLF -* All braces are on new lines -* The braces are enforced everywhere even if not explicitly required -* The multi statement constructions like *try-catch-finally* have each statement on new line -* Wrap code at 255 characters -* Tabs are substituted with 4 spaces -* Empty lines do not have tabs/spaces -* All new public methods have JavaDoc -* The JavaDoc should be compliant with [Java8 DocLint](http://openjdk.java.net/jeps/172) -* If the code is not self explanatory, then comments/JavaDoc should be added as appropriate. Excessive comments should be avoided. -* The strings which are shown to the user (in the UI) should be put in localization property bundles. Our localization team will handle the translations if required. - -##### Commit message -* Separate subject from body with a blank line -* Limit the subject line to 50 characters -* Capitalize the subject line -* Do not end the subject line with a period -* Use the imperative mood in the subject line -* Include JIRA numbers in the subject line -* Wrap the body at 72 characters -* Use the body to explain what and why vs. how -* Use a hyphen as a bullet point in the lists - -Example: -~~~ -ALF-12345 Summarize changes in around 50 characters or less - -More detailed explanatory text, if necessary. Wrap it to about 72 -characters or so. In some contexts, the first line is treated as the -subject of the commit and the rest of the text as the body. The -blank line separating the summary from the body is critical (unless -you omit the body entirely); various tools like `log`, `shortlog` -and `rebase` can get confused if you run the two together. - -Explain the problem that this commit is solving. Focus on why you -are making this change as opposed to how (the code explains that). -Are there side effects or other unintuitive consequences of this -change? Here's the place to explain them. - -Further paragraphs come after blank lines. - - - Bullet points are okay, too - - Use a hyphen for the bullet, preceded - by a single space -~~~ +Please follow the coding standards here: +https://community.alfresco.com/docs/DOC-4658-coding-standards From b7296f35c71033e12f81c7ca9701b6c0e8b4a034 Mon Sep 17 00:00:00 2001 From: Richard Esplin Date: Mon, 7 Aug 2017 19:49:32 -0600 Subject: [PATCH 045/282] Using markdown links. I changed my mind and decided that the links are so much cleaner hidden. --- CONTRIBUTING.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d8b80182d2..8d059e27ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,16 +5,12 @@ The following is a set of guidelines for contributing to this library. Most of t Because this project forms a part of Alfresco Content Services, the guidelines are hosted in the [Alfresco Social Community](http://community.alfresco.com/community/ecm) where they can be referenced from multiple projects. -An overview on how this project is goverened is here: -https://community.alfresco.com/docs/DOC-6385-project-overview-repository +Read an [overview on how this project is goverened](https://community.alfresco.com/docs/DOC-6385-project-overview-repository). You can report an issue in the ALF project of the [Alfresco issue tracker](http://issues.alfresco.com). -Instructions for a good issue report are here: -https://community.alfresco.com/docs/DOC-6263-reporting-an-issue +Read [instructions for a good issue report](https://community.alfresco.com/docs/DOC-6263-reporting-an-issue). -Instructions for making a contribution are here: -https://community.alfresco.com/docs/DOC-6269-submitting-contributions +Read [instructions for making a contribution](https://community.alfresco.com/docs/DOC-6269-submitting-contributions). -Please follow the coding standards here: -https://community.alfresco.com/docs/DOC-4658-coding-standards +Please follow [the coding standards](https://community.alfresco.com/docs/DOC-4658-coding-standards). From 815d5fc4d72ef94fa3e9d57dcf925788db0b31b4 Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Wed, 20 Sep 2017 14:49:33 +0100 Subject: [PATCH 046/282] REPO-2921 Put a .gitbugtraq file in all develop Branches --- .gitbugtraq | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitbugtraq diff --git a/.gitbugtraq b/.gitbugtraq new file mode 100644 index 0000000000..bacffb702d --- /dev/null +++ b/.gitbugtraq @@ -0,0 +1,4 @@ +# For SmartGit +[bugtraq "jira"] + url = https://issues.alfresco.com/jira/browse/%BUGID% + logRegex = ([A-Z]+-\\d+) From 1bb2cc97ca616e139eca5c73a0494c193d3ca575 Mon Sep 17 00:00:00 2001 From: alandavis Date: Tue, 10 Oct 2017 16:34:05 +0100 Subject: [PATCH 047/282] Fix/repo 2025 cat tests (#3) REPO-2025: adding the interface markers to categorise the tests --- .../util/testing/category/DBTests.java | 26 ++++++++++++++++ .../util/testing/category/DebugTests.java | 27 +++++++++++++++++ .../util/testing/category/LuceneTests.java | 29 ++++++++++++++++++ .../util/testing/category/NeverRunsTests.java | 28 +++++++++++++++++ .../util/testing/category/NonBuildTests.java | 26 ++++++++++++++++ .../testing/category/PerformanceTests.java | 28 +++++++++++++++++ .../util/testing/category/RedundantTests.java | 30 +++++++++++++++++++ .../util/testing/category/SanityTests.java | 27 +++++++++++++++++ .../util/testing/category/SlowTests.java | 26 ++++++++++++++++ 9 files changed, 247 insertions(+) create mode 100644 src/test/java/org/alfresco/util/testing/category/DBTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/DebugTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/LuceneTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/NonBuildTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/PerformanceTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/RedundantTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/SanityTests.java create mode 100644 src/test/java/org/alfresco/util/testing/category/SlowTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/DBTests.java b/src/test/java/org/alfresco/util/testing/category/DBTests.java new file mode 100644 index 0000000000..3c462fb57e --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/DBTests.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that test specific DB flavor functionality + */ +public interface DBTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/DebugTests.java b/src/test/java/org/alfresco/util/testing/category/DebugTests.java new file mode 100644 index 0000000000..886cee4d64 --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/DebugTests.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that are not meant to run every build. Only run by developers while working on a + * specific feature/class. They should not be added as part of test suites. + */ +public interface DebugTests extends NonBuildTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/LuceneTests.java b/src/test/java/org/alfresco/util/testing/category/LuceneTests.java new file mode 100644 index 0000000000..5b7a9d3702 --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/LuceneTests.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that use Lucene search. + * + * All tests marked with this category will eventually have to be transformed to not use lucene (mock the results if + * needed) or they will be move to another level of the testing scale: system/TAS testing; + */ +public interface LuceneTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java b/src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java new file mode 100644 index 0000000000..77162dcce7 --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that have been discovered not to be part of any test suites, but the cause of why they + * have not been added to a test suite is not clear. Investigation Jira tickets should also be created when marking + * these tests. + */ +public interface NeverRunsTests extends NonBuildTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/NonBuildTests.java b/src/test/java/org/alfresco/util/testing/category/NonBuildTests.java new file mode 100644 index 0000000000..7af1d804d1 --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/NonBuildTests.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Super category marker for tests that are not meant to run every build. + */ +public interface NonBuildTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/PerformanceTests.java b/src/test/java/org/alfresco/util/testing/category/PerformanceTests.java new file mode 100644 index 0000000000..ce16aa8132 --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/PerformanceTests.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that can't really be run on the build servers because they are meant to compare performance + * of some component on the same system. It usually requires manually running them locally for multiple times and + * comparing the results. + */ +public interface PerformanceTests extends NonBuildTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/RedundantTests.java b/src/test/java/org/alfresco/util/testing/category/RedundantTests.java new file mode 100644 index 0000000000..91ddbf930c --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/RedundantTests.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that are duplicate (test the same functionality as other tests), or tests that test + * deprecated functionality.

+ * + * Use the {@link NeverRunsTests} interface if the reason a test is not run is unknown. Also see {@link DebugTests}, + * {@link PerformanceTests} if the reason is known. + */ +public interface RedundantTests extends NonBuildTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/SanityTests.java b/src/test/java/org/alfresco/util/testing/category/SanityTests.java new file mode 100644 index 0000000000..181eb27eb0 --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/SanityTests.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that should quickly validate (about 20% of) the functionality very fast. The reviewer + * should really question any tests added to this category. + */ +public interface SanityTests +{ +} diff --git a/src/test/java/org/alfresco/util/testing/category/SlowTests.java b/src/test/java/org/alfresco/util/testing/category/SlowTests.java new file mode 100644 index 0000000000..2a5a7e6759 --- /dev/null +++ b/src/test/java/org/alfresco/util/testing/category/SlowTests.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2017 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.testing.category; + +/** + * Category marker for tests that mark slow tests. + */ +public interface SlowTests +{ +} From e6d123215e2a07e1ee1a1c24c670b6071212e663 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Tue, 10 Oct 2017 15:36:58 +0000 Subject: [PATCH 048/282] [maven-release-plugin] prepare release 6.14 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 86fe606b74..7d86c46d49 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.14-SNAPSHOT + 6.14 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.14 From 1b60852e464ec59badf6c545c2c49436ea55f04b Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Tue, 10 Oct 2017 15:37:04 +0000 Subject: [PATCH 049/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7d86c46d49..cb0a986e00 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 alfresco-core - 6.14 + 6.15-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.14 + HEAD From e1eeb64f00f31c075ec9e9f3cb750025d1ed7180 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Thu, 12 Oct 2017 16:04:32 +0100 Subject: [PATCH 050/282] ALF-21953: updated to use super pom v9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cb0a986e00..46adf15b73 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-super-pom - 8 + 9 alfresco-core 6.15-SNAPSHOT From a1ddb440fc726a91b4c1adce568ab02ed1a1bc9d Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Mon, 16 Oct 2017 20:42:43 +0300 Subject: [PATCH 051/282] REPO-2627 / REPO-2975: [Heartbeat] Add logging entry for the heartbeat package. --- src/main/resources/log4j.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index af37bef7db..15aba97fd5 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -266,3 +266,6 @@ log4j.logger.org.alfresco.repo.usage.RepoUsageMonitor=info # Authorization log4j.logger.org.alfresco.enterprise.repo.authorization.AuthorizationService=info log4j.logger.org.alfresco.enterprise.repo.authorization.AuthorizationsConsistencyMonitor=warn + +# HeartBeat +log4j.logger.org.alfresco.heartbeat=info From 9ecbdf08baf9c1a05eefc50bb5ef64afff99fe24 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 18 Oct 2017 08:10:47 +0000 Subject: [PATCH 052/282] [maven-release-plugin] prepare release 6.15 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 46adf15b73..55b45b9f88 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.15-SNAPSHOT + 6.15 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.15 From 1b485261d3534ce5dc03d9ad125b03861b3d12e7 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 18 Oct 2017 08:10:52 +0000 Subject: [PATCH 053/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 55b45b9f88..b7f673a9cb 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.15 + 6.16-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.15 + HEAD From cc5fea83bc5ae16eb45980815baeb1b993983b43 Mon Sep 17 00:00:00 2001 From: alandavis Date: Mon, 27 Nov 2017 16:46:02 +0000 Subject: [PATCH 054/282] Feature/repo 2908 jod (#4) REPO-2910 Replace ooo.direct code with jodconverter code Changes to core are limited to log4j entries that don't exist any more. --- src/main/resources/log4j.properties | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index 15aba97fd5..c9c313274d 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -58,9 +58,6 @@ log4j.logger.org.apache.myfaces.el.VariableResolverImpl=error log4j.logger.org.apache.myfaces.application.jsp.JspViewHandlerImpl=error log4j.logger.org.apache.myfaces.taglib=error -# OpenOfficeConnection -log4j.logger.net.sf.jooreports.openoffice.connection=fatal - # log prepared statement cache activity ### log4j.logger.org.hibernate.ps.PreparedStatementCache=info @@ -105,7 +102,6 @@ log4j.logger.org.alfresco.repo.admin.ConfigurationChecker=info log4j.logger.org.alfresco.repo.node.index.AbstractReindexComponent=warn log4j.logger.org.alfresco.repo.node.index.IndexTransactionTracker=warn log4j.logger.org.alfresco.repo.node.index.FullIndexRecoveryComponent=info -log4j.logger.org.alfresco.util.OpenOfficeConnectionTester=info log4j.logger.org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl=warn log4j.logger.org.alfresco.repo.domain.hibernate.DirtySessionMethodInterceptor=warn log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=warn From 8f736453e2f2d0abe2d24adb40037a8878ba2520 Mon Sep 17 00:00:00 2001 From: Younes Regaieg Date: Tue, 28 Nov 2017 15:18:19 +0100 Subject: [PATCH 055/282] ALF-21965 Force the use of UTC timezone in ISO8601DateFormat instead of defaulting to server's timezone --- src/main/java/org/alfresco/util/ISO8601DateFormat.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/src/main/java/org/alfresco/util/ISO8601DateFormat.java index ca0a460d7e..f2efd9e286 100644 --- a/src/main/java/org/alfresco/util/ISO8601DateFormat.java +++ b/src/main/java/org/alfresco/util/ISO8601DateFormat.java @@ -58,6 +58,7 @@ import org.joda.time.format.ISODateTimeFormat; public class ISO8601DateFormat { private static ThreadLocal> calendarThreadLocal = new ThreadLocal>(); + public static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); /** * Get a calendar object from cache. * @return calendar object from cache or newly created (if cache is empty) @@ -69,11 +70,11 @@ public class ISO8601DateFormat calendarThreadLocal.set(new HashMap()); } - Calendar calendar = calendarThreadLocal.get().get(TimeZone.getDefault()); + Calendar calendar = calendarThreadLocal.get().get(UTC_TIMEZONE); if (calendar == null) { - calendar = new GregorianCalendar(); - calendarThreadLocal.get().put(TimeZone.getDefault(), calendar); + calendar = new GregorianCalendar(UTC_TIMEZONE); + calendarThreadLocal.get().put(UTC_TIMEZONE, calendar); } return calendar; From b8fcff078d0538cb2ec2601935d7fac4498c0069 Mon Sep 17 00:00:00 2001 From: Younes Regaieg Date: Tue, 28 Nov 2017 15:27:49 +0100 Subject: [PATCH 056/282] ALF-21965 There is no longer a need to check for offset since we are using UTC --- .../org/alfresco/util/ISO8601DateFormat.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/src/main/java/org/alfresco/util/ISO8601DateFormat.java index f2efd9e286..a8fc354060 100644 --- a/src/main/java/org/alfresco/util/ISO8601DateFormat.java +++ b/src/main/java/org/alfresco/util/ISO8601DateFormat.java @@ -135,21 +135,7 @@ public class ISO8601DateFormat formatted.append(val); } - TimeZone tz = calendar.getTimeZone(); - int offset = tz.getOffset(calendar.getTimeInMillis()); - if (offset != 0) - { - int hours = Math.abs((offset / (60 * 1000)) / 60); - int minutes = Math.abs((offset / (60 * 1000)) % 60); - formatted.append(offset < 0 ? '-' : '+'); - formatted.append(hours < 10 ? ("0" + hours) : hours); - formatted.append(':'); - formatted.append(minutes < 10 ? ("0" + minutes) : minutes); - } - else - { - formatted.append('Z'); - } + formatted.append('Z'); return formatted.toString(); } From 53670a4a43932e6a44cad6063a487a86430d2d3f Mon Sep 17 00:00:00 2001 From: Younes Regaieg Date: Wed, 29 Nov 2017 15:08:51 +0100 Subject: [PATCH 057/282] ALF-21965 Make sure that the previously accessible method for getting a calendar object in system timezone is still returning a calendar for system default timezone, just in case some custom code was relying on it returning that. --- .../org/alfresco/util/ISO8601DateFormat.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/src/main/java/org/alfresco/util/ISO8601DateFormat.java index a8fc354060..267fcfd549 100644 --- a/src/main/java/org/alfresco/util/ISO8601DateFormat.java +++ b/src/main/java/org/alfresco/util/ISO8601DateFormat.java @@ -59,26 +59,37 @@ public class ISO8601DateFormat { private static ThreadLocal> calendarThreadLocal = new ThreadLocal>(); public static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); + /** * Get a calendar object from cache. + * @param timezone timezone object to indicate the timezone to be used by the returned calendar object * @return calendar object from cache or newly created (if cache is empty) */ - public static Calendar getCalendar() + public static Calendar getCalendar(TimeZone timezone) { if (calendarThreadLocal.get() == null) { calendarThreadLocal.set(new HashMap()); } - Calendar calendar = calendarThreadLocal.get().get(UTC_TIMEZONE); + Calendar calendar = calendarThreadLocal.get().get(timezone); if (calendar == null) { - calendar = new GregorianCalendar(UTC_TIMEZONE); - calendarThreadLocal.get().put(UTC_TIMEZONE, calendar); + calendar = new GregorianCalendar(timezone); + calendarThreadLocal.get().put(timezone, calendar); } return calendar; } + + /** + * Get a calendar object from cache for the system default timezone. + * @return calendar object from cache or newly created (if cache is empty) + */ + public static Calendar getCalendar() + { + return getCalendar(TimeZone.getDefault()); + } /** * Format date into ISO format (UCT0 / Zulu) @@ -88,7 +99,7 @@ public class ISO8601DateFormat */ public static String format(Date isoDate) { - Calendar calendar = getCalendar(); + Calendar calendar = getCalendar(UTC_TIMEZONE); calendar.setTime(isoDate); // MNT-9790 From 3481472d6e6fb3dfcedf09f4601e3e19b803f243 Mon Sep 17 00:00:00 2001 From: Younes Regaieg Date: Wed, 29 Nov 2017 15:20:08 +0100 Subject: [PATCH 058/282] ALF-21965 Add a test and comments to avoid future regressions --- .../org/alfresco/util/ISO8601DateFormat.java | 1 + .../alfresco/util/ISO8601DateFormatTest.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/src/main/java/org/alfresco/util/ISO8601DateFormat.java index 267fcfd549..28ddca5ae2 100644 --- a/src/main/java/org/alfresco/util/ISO8601DateFormat.java +++ b/src/main/java/org/alfresco/util/ISO8601DateFormat.java @@ -146,6 +146,7 @@ public class ISO8601DateFormat formatted.append(val); } + // ALF-21965 We are now confident we are using UTC timezone with zero offset formatted.append('Z'); return formatted.toString(); diff --git a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java index 96c23f4314..4156101d15 100644 --- a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java +++ b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java @@ -50,6 +50,24 @@ public class ISO8601DateFormatTest extends TestCase assertEquals(date, dateAfter); assertEquals(date2, dateAfter2); } + + public void testFormat() + { + TimeZone.setDefault(TimeZone.getTimeZone("PST")); // Any timezone other than UTC + + String strDate1 = "2005-09-16T17:01:03.456Z"; + String strDate2 = "1801-09-16T17:01:03.456Z"; + // convert to a date + Date date1 = ISO8601DateFormat.parse(strDate1); + Date date2 = ISO8601DateFormat.parse(strDate2); + // get the string form + String strDate1Formatted = ISO8601DateFormat.format(date1); + String strDate2Formatted = ISO8601DateFormat.format(date2); + + // Format needs to be in the UTC timezone with zero offset + assertEquals(strDate1, strDate1Formatted); + assertEquals(strDate2, strDate2Formatted); + } public void testGetCalendarMethod() { From 86e415aee78ac59126292afaf5f53b97d20c0285 Mon Sep 17 00:00:00 2001 From: Alexandru-Eusebiu Epure Date: Thu, 14 Dec 2017 13:39:24 +0200 Subject: [PATCH 059/282] REPO-3083 : ALF-21965: Issue with formatting dates in ISO8601 Remove extra spaces from newlines. --- .../org/alfresco/util/ISO8601DateFormat.java | 30 ++++----- .../alfresco/util/ISO8601DateFormatTest.java | 65 +++++++++---------- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/src/main/java/org/alfresco/util/ISO8601DateFormat.java index 28ddca5ae2..70034fb8e4 100644 --- a/src/main/java/org/alfresco/util/ISO8601DateFormat.java +++ b/src/main/java/org/alfresco/util/ISO8601DateFormat.java @@ -71,14 +71,14 @@ public class ISO8601DateFormat { calendarThreadLocal.set(new HashMap()); } - + Calendar calendar = calendarThreadLocal.get().get(timezone); if (calendar == null) { calendar = new GregorianCalendar(timezone); calendarThreadLocal.get().put(timezone, calendar); } - + return calendar; } @@ -90,7 +90,7 @@ public class ISO8601DateFormat { return getCalendar(TimeZone.getDefault()); } - + /** * Format date into ISO format (UCT0 / Zulu) * @@ -152,7 +152,7 @@ public class ISO8601DateFormat return formatted.toString(); } } - + /** * Normalise isoDate time to Zulu(UTC0) time-zone, removing any UTC offset. * @param isoDate @@ -170,7 +170,7 @@ public class ISO8601DateFormat throw new AlfrescoRuntimeException("Failed to parse date " + isoDate, e); } } - + /** * Parse date from ISO formatted string. * The ISO8601 date must include TimeZone offset information @@ -236,7 +236,7 @@ public class ISO8601DateFormat throw new AlfrescoRuntimeException("Failed to parse date " + isoDate, e); } } - + /** * Checks whether or not the given ISO8601-formatted date-string contains a time-component * instead of only the actual date. @@ -256,13 +256,13 @@ public class ISO8601DateFormat // Sign is included before year expectedLocation++; } - + defined = isoDate.length() >= expectedLocation && isoDate.charAt(expectedLocation) == 'T'; } return defined; } - + /** * Parses the given ISO8601-formatted date-string, not taking into account the time-component. * The time-information for the will be reset to zero. @@ -280,7 +280,7 @@ public class ISO8601DateFormat if(isoDate != null && isoDate.length() >= 10) { int offset = 0; - + // Sign can be included before year boolean bc = false; if(isoDate.charAt(0) == '-') @@ -292,14 +292,14 @@ public class ISO8601DateFormat { offset++; } - + // Extract year int year = Integer.parseInt(isoDate.substring(offset, offset += 4)); if (isoDate.charAt(offset) != '-') { throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset)); } - + // Extract month int month = Integer.parseInt(isoDate.substring(offset += 1, offset += 2)); if (isoDate.charAt(offset) != '-') @@ -309,7 +309,7 @@ public class ISO8601DateFormat // Extract day int day = Integer.parseInt(isoDate.substring(offset += 1, offset += 2)); - + Calendar calendar = new GregorianCalendar(timezone); calendar.setLenient(false); calendar.set(Calendar.YEAR, year); @@ -323,7 +323,7 @@ public class ISO8601DateFormat { calendar.set(Calendar.ERA, GregorianCalendar.BC); } - + return calendar.getTime(); } else @@ -340,6 +340,6 @@ public class ISO8601DateFormat throw new AlfrescoRuntimeException("Failed to parse date " + isoDate, e); } } - - + + } diff --git a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java index 4156101d15..5bd67d1162 100644 --- a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java +++ b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java @@ -68,18 +68,18 @@ public class ISO8601DateFormatTest extends TestCase assertEquals(strDate1, strDate1Formatted); assertEquals(strDate2, strDate2Formatted); } - + public void testGetCalendarMethod() { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); Calendar calendarGMT = ISO8601DateFormat.getCalendar(); - + TimeZone.setDefault(TimeZone.getTimeZone("BST")); Calendar calendarBST = ISO8601DateFormat.getCalendar(); - + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); Calendar calendarGMT1 = ISO8601DateFormat.getCalendar(); - + assertNotSame(calendarGMT, calendarBST); assertSame(calendarGMT, calendarGMT1); } @@ -148,17 +148,17 @@ public class ISO8601DateFormatTest extends TestCase String testBms = "2005-09-16T17:01:03.000Z"; String testC = "1801-09-16T17:01:03Z"; String testCms = "1801-09-16T17:01:03.000Z"; - + Date dateA = ISO8601DateFormat.parse(testA); Date dateB = ISO8601DateFormat.parse(testB); Date dateC = ISO8601DateFormat.parse(testC); - + assertEquals(testA, ISO8601DateFormat.format(dateA)); - + assertEquals(testBms, ISO8601DateFormat.format(dateB)); - + assertEquals(testCms, ISO8601DateFormat.format(dateC)); - + // The official ISO 8601.2004 spec doesn't say much helpful about milliseconds // The W3C version says it's up to different // implementations to put bounds on them @@ -168,64 +168,64 @@ public class ISO8601DateFormatTest extends TestCase String testCms5 = "2005-09-16T17:01:03.12345+01:00"; String testCms6 = "2005-09-16T17:01:03.123456+01:00"; String testCms7 = "2005-09-16T17:01:03.1234567+01:00"; - + Date testCDate = ISO8601DateFormat.parse(testCms3); assertEquals(testCDate, ISO8601DateFormat.parse(testCms4)); assertEquals(testCDate, ISO8601DateFormat.parse(testCms5)); assertEquals(testCDate, ISO8601DateFormat.parse(testCms6)); assertEquals(testCDate, ISO8601DateFormat.parse(testCms7)); } - + public void testTimezones() { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); Date date = null; - + TimeZone tz = TimeZone.getTimeZone("Australia/Sydney"); String testSydney = "2011-02-04T16:13:14"; String testUTC = "2011-02-04T05:13:14.000Z"; - + //Sydney date = ISO8601DateFormat.parse(testSydney, tz); assertEquals(testUTC, ISO8601DateFormat.format(date)); - + // Check with ms too date = ISO8601DateFormat.parse(testSydney + ".000", tz); assertEquals(testUTC, ISO8601DateFormat.format(date)); - + //Sydney with an offset and timezone date = ISO8601DateFormat.parse(testSydney+"+11:00", tz); assertEquals(testUTC, ISO8601DateFormat.format(date)); - + // Check with ms too date = ISO8601DateFormat.parse(testSydney + ".000"+"+11:00", tz); assertEquals(testUTC, ISO8601DateFormat.format(date)); } - + public void testToZulu(){ String base = "2011-02-04T16:13:14.000"; String zulu = base + "Z"; String utc0 = base + "+00:00"; String utc1 = "2011-02-04T17:13:14" + "+01:00"; String utcMinus1 = "2011-02-04T15:13:14" + "-01:00"; - + assertEquals(zulu, ISO8601DateFormat.formatToZulu(zulu)); assertEquals(zulu, ISO8601DateFormat.formatToZulu(utc1)); assertEquals(zulu, ISO8601DateFormat.formatToZulu(utc0)); assertEquals(zulu, ISO8601DateFormat.formatToZulu(utcMinus1)); } - + public void testDayOnly() { Date date = null; - + // Test simple parsing TimeZone tz = TimeZone.getTimeZone("Europe/London"); date = ISO8601DateFormat.parseDayOnly("2012-05-21", tz); - + Calendar cal = Calendar.getInstance(tz); cal.setTime(date); - + // Check date and time component assertEquals(2012, cal.get(Calendar.YEAR)); assertEquals(4, cal.get(Calendar.MONTH)); @@ -234,12 +234,12 @@ public class ISO8601DateFormatTest extends TestCase assertEquals(0, cal.get(Calendar.MINUTE)); assertEquals(0, cal.get(Calendar.SECOND)); assertEquals(0, cal.get(Calendar.MILLISECOND)); - + // Check time is ignored on full ISO8601-string date = ISO8601DateFormat.parseDayOnly("2012-05-21T12:13:14Z", tz); cal = Calendar.getInstance(tz); cal.setTime(date); - + assertEquals(2012, cal.get(Calendar.YEAR)); assertEquals(4, cal.get(Calendar.MONTH)); assertEquals(21, cal.get(Calendar.DAY_OF_MONTH)); @@ -247,19 +247,18 @@ public class ISO8601DateFormatTest extends TestCase assertEquals(0, cal.get(Calendar.MINUTE)); assertEquals(0, cal.get(Calendar.SECOND)); assertEquals(0, cal.get(Calendar.MILLISECOND)); - + // Check year signs date = ISO8601DateFormat.parseDayOnly("+2012-05-21", tz); cal = Calendar.getInstance(tz); cal.setTime(date); assertEquals(GregorianCalendar.AD, cal.get(Calendar.ERA)); - + date = ISO8601DateFormat.parseDayOnly("-2012-05-21", tz); cal = Calendar.getInstance(tz); cal.setTime(date); assertEquals(GregorianCalendar.BC, cal.get(Calendar.ERA)); - - + // Check illegal format try { @@ -274,7 +273,7 @@ public class ISO8601DateFormatTest extends TestCase } catch(AlfrescoRuntimeException e) {} } - + public void testDSTParser() { TimeZone tz = TimeZone.getTimeZone("America/Sao_Paulo"); @@ -282,21 +281,21 @@ public class ISO8601DateFormatTest extends TestCase // MNT-15454: This date is invalid as the 00:00 hour became 01:00 because of daylight saving time. String test1 = "2014-10-19T"; String test2 = "2014-10-19T00:01:01.000"; - + String isoFormattedDate = "2014-10-19T03:00:00.000Z"; - + // Sun Oct 19 01:00:00 BRST 2014 Date testDate = getDateValue(2014, 10, 19, 0, 0, 0, 0, - 3*60); // convert to a date Date date = ISO8601DateFormat.parse(test1, tz); // Check converted to date value assertEquals(testDate, date); - + // Convert to a date date = ISO8601DateFormat.parse(test2, tz); // Check converted to date value assertEquals(testDate, date); - + // Get the string form String strDate = ISO8601DateFormat.format(date); // Check the date converted to sting From 0a6340b3a313449caa12782356aa44e56d4ff333 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Tue, 19 Dec 2017 14:59:45 +0000 Subject: [PATCH 060/282] REPO-3144 Upgrade commons-codec 1.11 (#7) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b7f673a9cb..5a17e65fc8 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ commons-codec commons-codec - 1.10 + 1.11 commons-collections From 5e3f4171ab057f5dc6c05b58d5dff8f373a225ad Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 10 Jan 2018 12:57:33 +0000 Subject: [PATCH 061/282] [maven-release-plugin] prepare release 6.16 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5a17e65fc8..e78198c598 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.16-SNAPSHOT + 6.16 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.16 From 15c4d8bb03c1209236ec0c1fd4e765e9ec36eb7e Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 10 Jan 2018 12:57:38 +0000 Subject: [PATCH 062/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e78198c598..8d7e7e912a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.16 + 6.17-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.16 + HEAD From 68700633b03130970401543b6cac85c758d15fbd Mon Sep 17 00:00:00 2001 From: Andrei Rebegea Date: Tue, 23 Jan 2018 10:01:24 +0200 Subject: [PATCH 063/282] REPO-1066: update libs required by tika --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 8d7e7e912a..6493da37a9 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ commons-io commons-io - 2.4 + 2.6 org.apache.commons @@ -100,7 +100,7 @@ org.json json - 20160212 + 20171018 org.springframework @@ -165,7 +165,7 @@ org.apache.httpcomponents httpclient - 4.5.2 + 4.5.4 From 51f343442043f2241270348fb1bd32faf12602db Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Tue, 23 Jan 2018 08:23:07 +0000 Subject: [PATCH 064/282] [maven-release-plugin] prepare release 6.17_feature_repo-1066_tika_upgrade --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6493da37a9..7f18267825 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.17-SNAPSHOT + 6.17_feature_repo-1066_tika_upgrade Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.17_feature_repo-1066_tika_upgrade From ff6ad48d28a4208e69559a4050e919db2314dee5 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Tue, 23 Jan 2018 08:23:12 +0000 Subject: [PATCH 065/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7f18267825..6493da37a9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.17_feature_repo-1066_tika_upgrade + 6.17-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.17_feature_repo-1066_tika_upgrade + HEAD From 74de225372b6a2460653ad8dc22a4ca170f85fa2 Mon Sep 17 00:00:00 2001 From: Andrei Rebegea Date: Fri, 26 Jan 2018 18:35:45 +0200 Subject: [PATCH 066/282] REPO-1066 reverse json lib update as that would be removed in a separate epic --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6493da37a9..036ea391c8 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.json json - 20171018 + 20090211 org.springframework From e56caabceb9833963f0321c773085e6570206e03 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 26 Jan 2018 16:44:06 +0000 Subject: [PATCH 067/282] [maven-release-plugin] prepare release 6.17_feature_repo-1066_tika_upgrade_4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 036ea391c8..0226a913c5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.17-SNAPSHOT + 6.17_feature_repo-1066_tika_upgrade_4 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.17_feature_repo-1066_tika_upgrade_4 From 491e675d0e80094d521f0c030a7e1c97ad04c7a5 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 26 Jan 2018 16:44:13 +0000 Subject: [PATCH 068/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0226a913c5..036ea391c8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.17_feature_repo-1066_tika_upgrade_4 + 6.17-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.17_feature_repo-1066_tika_upgrade_4 + HEAD From ea9a5bf5116ea2d56345e2887280a224a21ef011 Mon Sep 17 00:00:00 2001 From: Andrei Rebegea Date: Thu, 1 Feb 2018 14:08:13 +0200 Subject: [PATCH 069/282] REPO-1066: use the default json lib in core --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 036ea391c8..c68cfc2e01 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.json json - 20090211 + 20160212 org.springframework From 45482b37c91eef01ecc166ce40eb2d129548d8f3 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 1 Feb 2018 12:20:10 +0000 Subject: [PATCH 070/282] [maven-release-plugin] prepare release 6.17 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c68cfc2e01..578c25ea4d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.17-SNAPSHOT + 6.17 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.17 From 1333b8d7fcaeaf2649a30e73f0b0e93a62e77ef5 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Thu, 1 Feb 2018 12:20:15 +0000 Subject: [PATCH 071/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 578c25ea4d..39a4366e3b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.17 + 6.18-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.17 + HEAD From 9ac5964fd37fd9762fb2d0ce3b52c3e494bcffe6 Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Fri, 23 Feb 2018 11:59:34 +0000 Subject: [PATCH 072/282] MNT-19261/REPO-3324 ISO8601DateFormat now handles Julian dates --- .../org/alfresco/util/ISO8601DateFormat.java | 9 ++- .../alfresco/util/ISO8601DateFormatTest.java | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/src/main/java/org/alfresco/util/ISO8601DateFormat.java index 70034fb8e4..5b8da9b283 100644 --- a/src/main/java/org/alfresco/util/ISO8601DateFormat.java +++ b/src/main/java/org/alfresco/util/ISO8601DateFormat.java @@ -27,10 +27,8 @@ import java.util.TimeZone; import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.error.AlfrescoRuntimeException; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.IllegalInstantException; -import org.joda.time.LocalDate; +import org.joda.time.*; +import org.joda.time.chrono.GJChronology; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; @@ -215,7 +213,8 @@ public class ISO8601DateFormat DateTimeZone dtz = DateTimeZone.forTimeZone(timezone); try { - DateTime dateTime = new DateTime(isoDate, dtz); + Chronology chrono = GJChronology.getInstance(dtz); + DateTime dateTime = new DateTime(isoDate, chrono); Date date = dateTime.toDate(); return date; } diff --git a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java index 5bd67d1162..0a59373ea3 100644 --- a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java +++ b/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java @@ -51,6 +51,63 @@ public class ISO8601DateFormatTest extends TestCase assertEquals(date2, dateAfter2); } + public void test19000101() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + String test = "1900-01-01"; + Date date = ISO8601DateFormat.parse(test); + String strDate = ISO8601DateFormat.format(date); + Date dateAfter = ISO8601DateFormat.parse(strDate); + assertEquals(date, dateAfter); + } + + public void test18991231() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + String test = "1899-12-31"; + Date date = ISO8601DateFormat.parse(test); + String strDate = ISO8601DateFormat.format(date); + Date dateAfter = ISO8601DateFormat.parse(strDate); + assertEquals(date, dateAfter); + } + + public void test18800207() + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + String test = "1880-02-07"; + Date date = ISO8601DateFormat.parse(test); + String strDate = ISO8601DateFormat.format(date); + Date dateAfter = ISO8601DateFormat.parse(strDate); + assertEquals(date, dateAfter); + } + + public void test15000207() // MNT-19261 JULIAN CALENDAR + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + String test = "1500-02-07"; + Date date = ISO8601DateFormat.parse(test); + String strDate = ISO8601DateFormat.format(date); // 1500-02-07T00:00:00.000Z + Date dateAfter = ISO8601DateFormat.parse(strDate); + // Before MNT-19261 Expected Wed Jan 29 03:06:28 GMT 1500 Actual Mon Jan 20 03:06:28 GMT 1500 + // After MNT-19261 Expected and Actual: Fri Feb 07 00:00:00 GMT 1500 + assertEquals(date, dateAfter); + } + + public void test10661014() // Battle of Hastings + { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + + String test = "10661014"; + Date date = ISO8601DateFormat.parse(test); + String strDate = ISO8601DateFormat.format(date); + Date dateAfter = ISO8601DateFormat.parse(strDate); + assertEquals(date, dateAfter); + } + public void testFormat() { TimeZone.setDefault(TimeZone.getTimeZone("PST")); // Any timezone other than UTC From 605b65730b352f32d16cb34e064e9a47df595385 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 23 Feb 2018 12:03:05 +0000 Subject: [PATCH 073/282] [maven-release-plugin] prepare release 6.18 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 39a4366e3b..158bcb172a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.18-SNAPSHOT + 6.18 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 6.18 From f199bff83b6b524999c1f13f595118bdf7a9feec Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 23 Feb 2018 12:03:11 +0000 Subject: [PATCH 074/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 158bcb172a..5e090fc093 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.18 + 6.19-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 6.18 + HEAD From 6a8f2b3987b6a25abc5378136c65a8b89126f5cd Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Sun, 4 Mar 2018 13:39:36 +0000 Subject: [PATCH 075/282] Upgrade Spring (REPO-1295), Quartz (REPO-3049) and remove Hibernate (REPO-2873) (#9) * The classes connected to ibatis/dialect were moved to alfresco-repository. * Major refactoring was done to TransactionSupportUtil class. It includes change of txn resource management. The new Spring 5 code cleans transaction synchronization once the txn is committed/rolled back. This means that "aftercompletion" listeners cannot manage txn resources they saved in earlier stages. The listeners can be caches, validors, locks, etc. In order to overcome the new behaviour a new ResourceHolder was added. The principle is very much similar to Spring internal resource holder. The object holds the current txn resources in ThreadLocal variable. If the txn is suspended another object is created and linked to previous resource holder so that once the txn is restored all of the resources are still in place. The resource holder is cleaned once all of the "aftercompletion" txn listeners have finished their work. * All the tests for this class are part of repository project. --- pom.xml | 9 +- .../alfresco/hibernate/DialectFactory.java | 177 ------ .../hibernate/DialectFactoryBean.java | 171 ------ .../java/org/alfresco/ibatis/BatchingDAO.java | 42 -- .../alfresco/ibatis/ByteArrayTypeHandler.java | 149 ----- .../HierarchicalSqlSessionFactoryBean.java | 552 ------------------ .../ibatis/HierarchicalXMLConfigBuilder.java | 395 ------------- .../java/org/alfresco/ibatis/IdsEntity.java | 76 --- .../ibatis/RetryingCallbackHelper.java | 154 ----- .../alfresco/ibatis/RollupResultHandler.java | 253 -------- .../ibatis/SerializableTypeHandler.java | 185 ------ .../alfresco/util/AbstractTriggerBean.java | 208 ------- .../org/alfresco/util/CronTriggerBean.java | 134 ----- .../java/org/alfresco/util/TriggerBean.java | 103 ---- .../AbstractAsynchronouslyRefreshedCache.java | 8 +- .../resource/HierarchicalResourceLoader.java | 203 ------- .../SpringAwareUserTransaction.java | 16 +- .../transaction/TransactionSupportUtil.java | 362 ++++++------ .../hibernate/DialectFactoryBeanTest.java | 66 --- ...HierarchicalSqlSessionFactoryBeanTest.java | 227 ------- .../util/bean/HierarchicalBeanLoaderTest.java | 95 --- .../HierarchicalResourceLoaderTest.java | 170 ------ .../hierarchical-bean-loader-test-context.xml | 44 -- .../hierarchy-test-SqlMapConfig.xml | 15 - .../hierarchy-test/hierarchy-test-context.xml | 51 -- .../hierarchy-test-SqlMap.xml | 26 - .../hierarchy-test-SqlMap.xml | 26 - .../hierarchy-test-SqlMap.xml | 26 - 28 files changed, 193 insertions(+), 3750 deletions(-) delete mode 100644 src/main/java/org/alfresco/hibernate/DialectFactory.java delete mode 100644 src/main/java/org/alfresco/hibernate/DialectFactoryBean.java delete mode 100644 src/main/java/org/alfresco/ibatis/BatchingDAO.java delete mode 100644 src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java delete mode 100644 src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java delete mode 100644 src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java delete mode 100644 src/main/java/org/alfresco/ibatis/IdsEntity.java delete mode 100644 src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java delete mode 100644 src/main/java/org/alfresco/ibatis/RollupResultHandler.java delete mode 100644 src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java delete mode 100644 src/main/java/org/alfresco/util/AbstractTriggerBean.java delete mode 100644 src/main/java/org/alfresco/util/CronTriggerBean.java delete mode 100644 src/main/java/org/alfresco/util/TriggerBean.java delete mode 100644 src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java delete mode 100644 src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java delete mode 100644 src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java delete mode 100644 src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java delete mode 100644 src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java delete mode 100644 src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml delete mode 100644 src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml delete mode 100644 src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml delete mode 100644 src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml delete mode 100644 src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml delete mode 100644 src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml diff --git a/pom.xml b/pom.xml index 5e090fc093..c53b7a8fde 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ - 3.2.16.RELEASE + 5.0.4.RELEASE 6.8 @@ -65,11 +65,6 @@ commons-math3 3.6.1 - - org.hibernate - hibernate - 3.2.6-alf-20131023 - org.safehaus.jug jug @@ -120,7 +115,7 @@ org.quartz-scheduler quartz - 1.8.3-alfresco-patched + 2.3.0 org.alfresco.surf diff --git a/src/main/java/org/alfresco/hibernate/DialectFactory.java b/src/main/java/org/alfresco/hibernate/DialectFactory.java deleted file mode 100644 index eb143b8c9d..0000000000 --- a/src/main/java/org/alfresco/hibernate/DialectFactory.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 2011 - Alfresco Software, Ltd. - * This file was copied from org.hibernate.dialect.DialectFactory - */ -package org.alfresco.hibernate; - -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import org.hibernate.HibernateException; -import org.hibernate.cfg.Environment; -import org.hibernate.dialect.Dialect; -import org.hibernate.util.ReflectHelper; - -/** - * A factory for generating Dialect instances. - * - * @author Steve Ebersole - * @author Alfresco - */ -public class DialectFactory { - - /** - * Builds an appropriate Dialect instance. - *

- * If a dialect is explicitly named in the incoming properties, it is used. Otherwise, the database name and version - * (obtained from connection metadata) are used to make the dertemination. - *

- * An exception is thrown if a dialect was not explicitly set and the database name is not known. - * - * @param props The configuration properties. - * @param databaseName The name of the database product (obtained from metadata). - * @param databaseMajorVersion The major version of the database product (obtained from metadata). - * - * @return The appropriate dialect. - * - * @throws HibernateException No dialect specified and database name not known. - */ - public static Dialect buildDialect(Properties props, String databaseName, int databaseMajorVersion) - throws HibernateException { - String dialectName = props.getProperty( Environment.DIALECT ); - if ( dialectName == null || dialectName.length() == 0) { - return determineDialect( databaseName, databaseMajorVersion ); - } - else { - // Push the dialect onto the system properties - System.setProperty(Environment.DIALECT, dialectName); - return buildDialect( dialectName ); - } - } - - /** - * Determine the appropriate Dialect to use given the database product name - * and major version. - * - * @param databaseName The name of the database product (obtained from metadata). - * @param databaseMajorVersion The major version of the database product (obtained from metadata). - * - * @return An appropriate dialect instance. - */ - public static Dialect determineDialect(String databaseName, int databaseMajorVersion) { - if ( databaseName == null ) { - throw new HibernateException( "Hibernate Dialect must be explicitly set" ); - } - - DatabaseDialectMapper mapper = ( DatabaseDialectMapper ) MAPPERS.get( databaseName ); - if ( mapper == null ) { - throw new HibernateException( "Hibernate Dialect must be explicitly set for database: " + databaseName ); - } - - String dialectName = mapper.getDialectClass( databaseMajorVersion ); - // Push the dialect onto the system properties - System.setProperty(Environment.DIALECT, dialectName); - return buildDialect( dialectName ); - } - - /** - * Returns a dialect instance given the name of the class to use. - * - * @param dialectName The name of the dialect class. - * - * @return The dialect instance. - */ - public static Dialect buildDialect(String dialectName) { - try { - return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance(); - } - catch ( ClassNotFoundException cnfe ) { - throw new HibernateException( "Dialect class not found: " + dialectName ); - } - catch ( Exception e ) { - throw new HibernateException( "Could not instantiate dialect class", e ); - } - } - - /** - * For a given database product name, instances of - * DatabaseDialectMapper know which Dialect to use for different versions. - */ - public static interface DatabaseDialectMapper { - public String getDialectClass(int majorVersion); - } - - /** - * A simple DatabaseDialectMapper for dialects which are independent - * of the underlying database product version. - */ - public static class VersionInsensitiveMapper implements DatabaseDialectMapper { - private String dialectClassName; - - public VersionInsensitiveMapper(String dialectClassName) { - this.dialectClassName = dialectClassName; - } - - public String getDialectClass(int majorVersion) { - return dialectClassName; - } - } - - // TODO : this is the stuff it'd be nice to move to a properties file or some other easily user-editable place - private static final Map MAPPERS = new HashMap(); - static { - // detectors... - MAPPERS.put( "HSQL Database Engine", new VersionInsensitiveMapper( "org.hibernate.dialect.HSQLDialect" ) ); - MAPPERS.put( "H2", new VersionInsensitiveMapper( "org.hibernate.dialect.H2Dialect" ) ); - MAPPERS.put( "MySQL", new VersionInsensitiveMapper( "org.hibernate.dialect.MySQLDialect" ) ); - MAPPERS.put( "PostgreSQL", new VersionInsensitiveMapper( "org.hibernate.dialect.PostgreSQLDialect" ) ); - MAPPERS.put( "Apache Derby", new VersionInsensitiveMapper( "org.hibernate.dialect.DerbyDialect" ) ); - - MAPPERS.put( "Ingres", new VersionInsensitiveMapper( "org.hibernate.dialect.IngresDialect" ) ); - MAPPERS.put( "ingres", new VersionInsensitiveMapper( "org.hibernate.dialect.IngresDialect" ) ); - MAPPERS.put( "INGRES", new VersionInsensitiveMapper( "org.hibernate.dialect.IngresDialect" ) ); - - MAPPERS.put( "Microsoft SQL Server Database", new VersionInsensitiveMapper( "org.hibernate.dialect.SQLServerDialect" ) ); - MAPPERS.put( "Microsoft SQL Server", new VersionInsensitiveMapper( "org.hibernate.dialect.SQLServerDialect" ) ); - MAPPERS.put( "Sybase SQL Server", new VersionInsensitiveMapper( "org.hibernate.dialect.SybaseDialect" ) ); - MAPPERS.put( "Adaptive Server Enterprise", new VersionInsensitiveMapper( "org.hibernate.dialect.SybaseDialect" ) ); - - MAPPERS.put( "Informix Dynamic Server", new VersionInsensitiveMapper( "org.hibernate.dialect.InformixDialect" ) ); - - MAPPERS.put( "DB2/NT", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2/LINUX", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2/6000", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2/HPUX", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2/SUN", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2/LINUX390", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2/AIX64", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/NT",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/NT64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2 UDP",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/LINUX",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/LINUX390",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/LINUXZ64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/400 SQL",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/6000",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2 UDB iSeries",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/AIX64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/HPUX",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/HP64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/SUN",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/SUN64",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/PTX",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/2",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - MAPPERS.put( "DB2/LINUXX8664",new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" )); - - MAPPERS.put( "MySQL", new VersionInsensitiveMapper( "org.hibernate.dialect.MySQLInnoDBDialect" ) ); - MAPPERS.put( "DB2/NT64", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "DB2/LINUX", new VersionInsensitiveMapper( "org.hibernate.dialect.DB2Dialect" ) ); - MAPPERS.put( "Microsoft SQL Server Database", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect" ) ); - MAPPERS.put( "Microsoft SQL Server", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect" ) ); - MAPPERS.put( "Sybase SQL Server", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect" ) ); - MAPPERS.put( "Oracle", new VersionInsensitiveMapper( "org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect" ) ); - } -} diff --git a/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java b/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java deleted file mode 100644 index 92cbe6a418..0000000000 --- a/src/main/java/org/alfresco/hibernate/DialectFactoryBean.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.hibernate; - -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.SQLException; -import java.util.Map; -import java.util.Properties; - -import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.hibernate.cfg.Configuration; -import org.hibernate.cfg.Environment; -import org.hibernate.dialect.Dialect; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.orm.hibernate3.LocalSessionFactoryBean; - -/** - * Factory for the Hibernate dialect. Allows dialect detection logic to be centralized and the dialect to be injected - * where required as a singleton from the container. - * - * @author dward - */ -public class DialectFactoryBean implements FactoryBean -{ - - /** The local session factory. */ - private LocalSessionFactoryBean localSessionFactory; - - /** The map used in mapping driver name to a dialect. **/ - private Map driverDialectMap; - - - public void setDriverDialectMap(Map driverDialectMap) - { - this.driverDialectMap = driverDialectMap; - - } - - public Map getDriverDialectMap() - { - return this.driverDialectMap; - } - - - - - /** - * Sets the local session factory. - * - * @param localSessionFactory - * the new local session factory - */ - public void setLocalSessionFactory(LocalSessionFactoryBean localSessionFactory) - { - this.localSessionFactory = localSessionFactory; - } - - @SuppressWarnings("deprecation") - @Override - public Dialect getObject() throws SQLException - { - Session session = ((SessionFactory) this.localSessionFactory.getObject()).openSession(); - Configuration cfg = this.localSessionFactory.getConfiguration(); - Connection con = null; - try - { - // make sure that we AUTO-COMMIT - con = session.connection(); - con.setAutoCommit(true); - DatabaseMetaData meta = con.getMetaData(); - - overrideDialectPropertyForDriver(cfg.getProperties(), meta.getDriverName()); - - Dialect dialect = DialectFactory.buildDialect(cfg.getProperties(), meta.getDatabaseProductName(), meta.getDatabaseMajorVersion()); - - dialect = changeDialect(cfg, dialect); - return dialect; - } - finally - { - try - { - con.close(); - } - catch (Exception e) - { - } - } - } - - - /** - * Override or add a dialect property to props based on the driver name - */ - void overrideDialectPropertyForDriver(Properties props, String driverName) - { - if (driverDialectMap != null && driverDialectMap.containsKey(driverName)) - { - props.setProperty(Environment.DIALECT, driverDialectMap.get(driverName)); - } - } - - /** - * Substitute the dialect with an alternative, if possible. - * - * @param cfg - * the configuration - * @param dialect - * the dialect - * @return the dialect - */ - private Dialect changeDialect(Configuration cfg, Dialect dialect) - { - String dialectName = cfg.getProperty(Environment.DIALECT); - if (dialectName == null || dialectName.length() == 0) - { - // Fix the dialect property to match the detected dialect - cfg.setProperty(Environment.DIALECT, dialect.getClass().getName()); - } - return dialect; - // TODO: https://issues.alfresco.com/jira/browse/ETHREEOH-679 - // else if (dialectName.equals(Oracle9Dialect.class.getName())) - // { - // String subst = AlfrescoOracle9Dialect.class.getName(); - // LogUtil.warn(logger, WARN_DIALECT_SUBSTITUTING, dialectName, subst); - // cfg.setProperty(Environment.DIALECT, subst); - // } - // else if (dialectName.equals(MySQLDialect.class.getName())) - // { - // String subst = MySQLInnoDBDialect.class.getName(); - // LogUtil.warn(logger, WARN_DIALECT_SUBSTITUTING, dialectName, subst); - // cfg.setProperty(Environment.DIALECT, subst); - // } - // else if (dialectName.equals(MySQL5Dialect.class.getName())) - // { - // String subst = MySQLInnoDBDialect.class.getName(); - // LogUtil.warn(logger, WARN_DIALECT_SUBSTITUTING, dialectName, subst); - // cfg.setProperty(Environment.DIALECT, subst); - // } - } - - @Override - public Class getObjectType() - { - return Dialect.class; - } - - @Override - public boolean isSingleton() - { - return true; - } -} diff --git a/src/main/java/org/alfresco/ibatis/BatchingDAO.java b/src/main/java/org/alfresco/ibatis/BatchingDAO.java deleted file mode 100644 index fc35dfce98..0000000000 --- a/src/main/java/org/alfresco/ibatis/BatchingDAO.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -/** - * Interface for DAOs that offer batching. This should be provided as an optimization - * and DAO implementations that can't supply batching should just do nothing. - * - * @author Derek Hulley - * @since 3.2.1 - */ -public interface BatchingDAO -{ - /** - * Start a batch of insert or update commands - * - * @throws RuntimeException wrapping a SQLException - */ - void startBatch(); - /** - * Write a batch of insert or update commands - * - * @throws RuntimeException wrapping a SQLException - */ - void executeBatch(); -} diff --git a/src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java b/src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java deleted file mode 100644 index 406363569e..0000000000 --- a/src/main/java/org/alfresco/ibatis/ByteArrayTypeHandler.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.Serializable; -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; - -import org.alfresco.ibatis.SerializableTypeHandler.DeserializationException; -import org.apache.ibatis.type.JdbcType; -import org.apache.ibatis.type.TypeHandler; - -/** - * MyBatis 3.x TypeHandler for _byte[] to BLOB types. - * - * @author sglover - * @since 5.0 - */ -public class ByteArrayTypeHandler implements TypeHandler -{ - /** - * @throws DeserializationException if the object could not be deserialized - */ - public Object getResult(ResultSet rs, String columnName) throws SQLException - { - byte[] ret = null; - try - { - byte[] bytes = rs.getBytes(columnName); - if(bytes != null && !rs.wasNull()) - { - ret = bytes; - } - } - catch (Throwable e) - { - throw new DeserializationException(e); - } - return ret; - } - - @Override - public Object getResult(ResultSet rs, int columnIndex) throws SQLException - { - byte[] ret = null; - try - { - byte[] bytes = rs.getBytes(columnIndex); - if(bytes != null && !rs.wasNull()) - { - ret = bytes; - } - } - catch (Throwable e) - { - throw new DeserializationException(e); - } - return ret; - } - - public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException - { - if (parameter == null) - { - ps.setNull(i, Types.BINARY); - } - else - { - try - { - ps.setBytes(i, (byte[])parameter); - } - catch (Throwable e) - { - throw new SerializationException(e); - } - } - } - - public Object getResult(CallableStatement cs, int columnIndex) throws SQLException - { - throw new UnsupportedOperationException("Unsupported"); - } - - /** - * @return Returns the value given - */ - public Object valueOf(String s) - { - return s; - } - - /** - * Marker exception to allow deserialization issues to be dealt with by calling code. - * If this exception remains uncaught, it will be very difficult to find and rectify - * the data issue. - * - * @author sglover - * @since 5.0 - */ - public static class DeserializationException extends RuntimeException - { - private static final long serialVersionUID = 4673487701048985340L; - - public DeserializationException(Throwable cause) - { - super(cause); - } - } - - /** - * Marker exception to allow serialization issues to be dealt with by calling code. - * Unlike with {@link DeserializationException deserialization}, it is not important - * to handle this exception neatly. - * - * @author sglover - * @since 5.0 - */ - public static class SerializationException extends RuntimeException - { - private static final long serialVersionUID = 962957884262870228L; - - public SerializationException(Throwable cause) - { - super(cause); - } - } -} diff --git a/src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java b/src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java deleted file mode 100644 index fc056594eb..0000000000 --- a/src/main/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBean.java +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import java.io.IOException; -import java.util.Properties; - -import javax.sql.DataSource; - -import org.alfresco.util.PropertyCheck; -import org.alfresco.util.resource.HierarchicalResourceLoader; -import org.apache.ibatis.builder.xml.XMLMapperBuilder; -import org.apache.ibatis.executor.ErrorContext; -import org.apache.ibatis.logging.Log; -import org.apache.ibatis.logging.LogFactory; -import org.apache.ibatis.mapping.DatabaseIdProvider; -import org.apache.ibatis.mapping.Environment; -import org.apache.ibatis.mapping.VendorDatabaseIdProvider; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.reflection.factory.ObjectFactory; -import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.ibatis.session.SqlSessionFactoryBuilder; -import org.apache.ibatis.transaction.TransactionFactory; -import org.apache.ibatis.type.TypeHandler; -import org.mybatis.spring.SqlSessionFactoryBean; -import org.mybatis.spring.transaction.SpringManagedTransactionFactory; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.core.NestedIOException; -import org.springframework.core.io.Resource; -import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; - -import static org.springframework.util.Assert.notNull; -import static org.springframework.util.ObjectUtils.isEmpty; -import static org.springframework.util.StringUtils.hasLength; -import static org.springframework.util.StringUtils.tokenizeToStringArray; - -/** - * Extends the MyBatis-Spring support by allowing a choice of {@link org.springframework.core.io.ResourceLoader}. The - * {@link #setResourceLoader(HierarchicalResourceLoader) ResourceLoader} will be used to load the SqlMapConfig - * file and use a {@link HierarchicalXMLConfigBuilder} to read the individual MyBatis (3.x) resources. - *

- * Pending a better way to extend/override, much of the implementation is a direct copy of the MyBatis-Spring - * {@link SqlSessionFactoryBean}; some of the protected methods do not have access to the object's state - * and can therefore not be overridden successfully. - *

- * This is equivalent to HierarchicalSqlMapClientFactoryBean which extended iBatis (2.x). - * See also: IBATIS-589 - * and: - * - * @author Derek Hulley, janv - * @since 4.0 - */ -//note: effectively replaces SqlSessionFactoryBean to use hierarchical resource loader -public class HierarchicalSqlSessionFactoryBean extends SqlSessionFactoryBean -{ - - private HierarchicalResourceLoader resourceLoader; - - private final Log logger = LogFactory.getLog(getClass()); - - private Resource configLocation; - - private Resource[] mapperLocations; - - private DataSource dataSource; - - private TransactionFactory transactionFactory; - - private Properties configurationProperties; - - private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); - - private SqlSessionFactory sqlSessionFactory; - - private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1 - - private boolean failFast; - - private Interceptor[] plugins; - - private TypeHandler[] typeHandlers; - - private String typeHandlersPackage; - - private Class[] typeAliases; - - private String typeAliasesPackage; - - private Class typeAliasesSuperType; - - private DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider(); - - private ObjectFactory objectFactory; - - private ObjectWrapperFactory objectWrapperFactory; - /** - * Default constructor - */ - public HierarchicalSqlSessionFactoryBean() - { - } - - /** - * Set the resource loader to use. To use the #resource.dialect# placeholder, use the - * {@link HierarchicalResourceLoader}. - * - * @param resourceLoader the resource loader to use - */ - public void setResourceLoader(HierarchicalResourceLoader resourceLoader) - { - this.resourceLoader = resourceLoader; - } - - /** - * Sets the ObjectFactory. - * - * @since 1.1.2 - * @param objectFactory ObjectFactory - */ - public void setObjectFactory(ObjectFactory objectFactory) { - this.objectFactory = objectFactory; - } - - /** - * Sets the ObjectWrapperFactory. - * - * @since 1.1.2 - * @param objectWrapperFactory ObjectWrapperFactory - */ - public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) { - this.objectWrapperFactory = objectWrapperFactory; - } - - /** - * Sets the DatabaseIdProvider. - * - * @since 1.1.0 - * @return DatabaseIdProvider - */ - public DatabaseIdProvider getDatabaseIdProvider() { - return databaseIdProvider; - } - - /** - * Gets the DatabaseIdProvider - * - * @since 1.1.0 - * @param databaseIdProvider DatabaseIdProvider - */ - public void setDatabaseIdProvider(DatabaseIdProvider databaseIdProvider) { - this.databaseIdProvider = databaseIdProvider; - } - - /** - * Mybatis plugin list. - * - * @since 1.0.1 - * - * @param plugins list of plugins - * - */ - public void setPlugins(Interceptor[] plugins) { - this.plugins = plugins; - } - - /** - * Packages to search for type aliases. - * - * @since 1.0.1 - * - * @param typeAliasesPackage package to scan for domain objects - * - */ - public void setTypeAliasesPackage(String typeAliasesPackage) { - this.typeAliasesPackage = typeAliasesPackage; - } - - /** - * Super class which domain objects have to extend to have a type alias created. - * No effect if there is no package to scan configured. - * - * @since 1.1.2 - * - * @param typeAliasesSuperType super class for domain objects - * - */ - public void setTypeAliasesSuperType(Class typeAliasesSuperType) { - this.typeAliasesSuperType = typeAliasesSuperType; - } - - /** - * Packages to search for type handlers. - * - * @since 1.0.1 - * - * @param typeHandlersPackage package to scan for type handlers - * - */ - public void setTypeHandlersPackage(String typeHandlersPackage) { - this.typeHandlersPackage = typeHandlersPackage; - } - - /** - * Set type handlers. They must be annotated with {@code MappedTypes} and optionally with {@code MappedJdbcTypes} - * - * @since 1.0.1 - * - * @param typeHandlers Type handler list - */ - public void setTypeHandlers(TypeHandler[] typeHandlers) { - this.typeHandlers = typeHandlers; - } - - /** - * List of type aliases to register. They can be annotated with {@code Alias} - * - * @since 1.0.1 - * - * @param typeAliases Type aliases list - */ - public void setTypeAliases(Class[] typeAliases) { - this.typeAliases = typeAliases; - } - - /** - * If true, a final check is done on Configuration to assure that all mapped - * statements are fully loaded and there is no one still pending to resolve - * includes. Defaults to false. - * - * @since 1.0.1 - * - * @param failFast enable failFast - */ - public void setFailFast(boolean failFast) { - this.failFast = failFast; - } - - /** - * Set the location of the MyBatis {@code SqlSessionFactory} config file. A typical value is - * "WEB-INF/mybatis-configuration.xml". - */ - public void setConfigLocation(Resource configLocation) { - this.configLocation = configLocation; - } - - /** - * Set locations of MyBatis mapper files that are going to be merged into the {@code SqlSessionFactory} - * configuration at runtime. - * - * This is an alternative to specifying "<sqlmapper>" entries in an MyBatis config file. - * This property being based on Spring's resource abstraction also allows for specifying - * resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml". - */ - public void setMapperLocations(Resource[] mapperLocations) { - this.mapperLocations = mapperLocations; - } - - /** - * Set optional properties to be passed into the SqlSession configuration, as alternative to a - * {@code <properties>} tag in the configuration xml file. This will be used to - * resolve placeholders in the config file. - */ - public void setConfigurationProperties(Properties sqlSessionFactoryProperties) { - this.configurationProperties = sqlSessionFactoryProperties; - } - - /** - * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} - * should match the one used by the {@code SqlSessionFactory}: for example, you could specify the same - * JNDI DataSource for both. - * - * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code - * accessing this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}. - * - * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not - * a {@code TransactionAwareDataSourceProxy}. Only data access code may work with - * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the - * underlying target {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} - * passed in, it will be unwrapped to extract its target {@code DataSource}. - * - */ - public void setDataSource(DataSource dataSource) { - if (dataSource instanceof TransactionAwareDataSourceProxy) { - // If we got a TransactionAwareDataSourceProxy, we need to perform - // transactions for its underlying target DataSource, else data - // access code won't see properly exposed transactions (i.e. - // transactions for the target DataSource). - this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); - } else { - this.dataSource = dataSource; - } - } - - /** - * Sets the {@code SqlSessionFactoryBuilder} to use when creating the {@code SqlSessionFactory}. - * - * This is mainly meant for testing so that mock SqlSessionFactory classes can be injected. By - * default, {@code SqlSessionFactoryBuilder} creates {@code DefaultSqlSessionFactory} instances. - * - */ - public void setSqlSessionFactoryBuilder(SqlSessionFactoryBuilder sqlSessionFactoryBuilder) { - this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder; - } - - /** - * Set the MyBatis TransactionFactory to use. Default is {@code SpringManagedTransactionFactory} - * - * The default {@code SpringManagedTransactionFactory} should be appropriate for all cases: - * be it Spring transaction management, EJB CMT or plain JTA. If there is no active transaction, - * SqlSession operations will execute SQL statements non-transactionally. - * - * It is strongly recommended to use the default {@code TransactionFactory}. If not used, any - * attempt at getting an SqlSession through Spring's MyBatis framework will throw an exception if - * a transaction is active. - * - * @see SpringManagedTransactionFactory - * @param transactionFactory the MyBatis TransactionFactory - */ - public void setTransactionFactory(TransactionFactory transactionFactory) { - this.transactionFactory = transactionFactory; - } - - /** - * NOTE: This class overrides any {@code Environment} you have set in the MyBatis - * config file. This is used only as a placeholder name. The default value is - * {@code SqlSessionFactoryBean.class.getSimpleName()}. - * - * @param environment the environment name - */ - public void setEnvironment(String environment) { - this.environment = environment; - } - - @Override - public void afterPropertiesSet() throws Exception { - - PropertyCheck.mandatory(this, "resourceLoader", resourceLoader); - - notNull(dataSource, "Property 'dataSource' is required"); - notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); - - this.sqlSessionFactory = buildSqlSessionFactory(); - } - - - - - /** - * Build a {@code SqlSessionFactory} instance. - *

- * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a - * {@code SqlSessionFactory} instance based on an Reader. - * - * @return SqlSessionFactory - * @throws IOException if loading the config file failed - */ - protected SqlSessionFactory buildSqlSessionFactory() throws IOException { - - Configuration configuration; - - HierarchicalXMLConfigBuilder xmlConfigBuilder = null; - if (this.configLocation != null) { - try { - xmlConfigBuilder = new HierarchicalXMLConfigBuilder(resourceLoader, this.configLocation.getInputStream(), null, this.configurationProperties); - configuration = xmlConfigBuilder.getConfiguration(); - } catch (Exception ex) { - throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); - } finally { - ErrorContext.instance().reset(); - } - } else { - if (this.logger.isDebugEnabled()) { - this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration"); - } - configuration = new Configuration(); - configuration.setVariables(this.configurationProperties); - } - - if (this.objectFactory != null) { - configuration.setObjectFactory(this.objectFactory); - } - - if (this.objectWrapperFactory != null) { - configuration.setObjectWrapperFactory(this.objectWrapperFactory); - } - - if (hasLength(this.typeAliasesPackage)) { - String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, - ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); - for (String packageToScan : typeAliasPackageArray) { - configuration.getTypeAliasRegistry().registerAliases(packageToScan, - typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); - if (this.logger.isDebugEnabled()) { - this.logger.debug("Scanned package: '" + packageToScan + "' for aliases"); - } - } - } - - if (!isEmpty(this.typeAliases)) { - for (Class typeAlias : this.typeAliases) { - configuration.getTypeAliasRegistry().registerAlias(typeAlias); - if (this.logger.isDebugEnabled()) { - this.logger.debug("Registered type alias: '" + typeAlias + "'"); - } - } - } - - if (!isEmpty(this.plugins)) { - for (Interceptor plugin : this.plugins) { - configuration.addInterceptor(plugin); - if (this.logger.isDebugEnabled()) { - this.logger.debug("Registered plugin: '" + plugin + "'"); - } - } - } - - if (hasLength(this.typeHandlersPackage)) { - String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage, - ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); - for (String packageToScan : typeHandlersPackageArray) { - configuration.getTypeHandlerRegistry().register(packageToScan); - if (this.logger.isDebugEnabled()) { - this.logger.debug("Scanned package: '" + packageToScan + "' for type handlers"); - } - } - } - - if (!isEmpty(this.typeHandlers)) { - for (TypeHandler typeHandler : this.typeHandlers) { - configuration.getTypeHandlerRegistry().register(typeHandler); - if (this.logger.isDebugEnabled()) { - this.logger.debug("Registered type handler: '" + typeHandler + "'"); - } - } - } - - if (xmlConfigBuilder != null) { - try { - xmlConfigBuilder.parse(); - - if (this.logger.isDebugEnabled()) { - this.logger.debug("Parsed configuration file: '" + this.configLocation + "'"); - } - } catch (Exception ex) { - throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex); - } finally { - ErrorContext.instance().reset(); - } - } - - if (this.transactionFactory == null) { - this.transactionFactory = new SpringManagedTransactionFactory(); - } - - Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource); - configuration.setEnvironment(environment); - - //Commented out to be able to use dummy dataSource in tests. - /* - if (this.databaseIdProvider != null) { - try { - configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); - } catch (SQLException e) { - throw new NestedIOException("Failed getting a databaseId", e); - } - } - */ - - if (!isEmpty(this.mapperLocations)) { - for (Resource mapperLocation : this.mapperLocations) { - if (mapperLocation == null) { - continue; - } - - try { - XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), - configuration, mapperLocation.toString(), configuration.getSqlFragments()); - xmlMapperBuilder.parse(); - } catch (Exception e) { - throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); - } finally { - ErrorContext.instance().reset(); - } - - if (this.logger.isDebugEnabled()) { - this.logger.debug("Parsed mapper file: '" + mapperLocation + "'"); - } - } - } else { - if (this.logger.isDebugEnabled()) { - this.logger.debug("Property 'mapperLocations' was not specified, only MyBatis mapper files specified in the config xml were loaded"); - } - } - - return this.sqlSessionFactoryBuilder.build(configuration); - } - - /** - * {@inheritDoc} - */ - public SqlSessionFactory getObject() throws Exception { - if (this.sqlSessionFactory == null) { - afterPropertiesSet(); - } - - return this.sqlSessionFactory; - } - - /** - * {@inheritDoc} - */ - public Class getObjectType() { - return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass(); - } - - /** - * {@inheritDoc} - */ - public boolean isSingleton() { - return true; - } - - /** - * {@inheritDoc} - */ - public void onApplicationEvent(ApplicationEvent event) { - if (failFast && event instanceof ContextRefreshedEvent) { - // fail-fast -> check all statements are completed - this.sqlSessionFactory.getConfiguration().getMappedStatementNames(); - } - } -} diff --git a/src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java b/src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java deleted file mode 100644 index 89d1ecf3da..0000000000 --- a/src/main/java/org/alfresco/ibatis/HierarchicalXMLConfigBuilder.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (C) 2005-2016 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import java.io.InputStream; -import java.util.Properties; - -import javax.sql.DataSource; - -import org.alfresco.util.resource.HierarchicalResourceLoader; -import org.apache.ibatis.builder.BaseBuilder; -import org.apache.ibatis.builder.BuilderException; -import org.apache.ibatis.builder.xml.XMLMapperBuilder; -import org.apache.ibatis.builder.xml.XMLMapperEntityResolver; -import org.apache.ibatis.datasource.DataSourceFactory; -import org.apache.ibatis.executor.ErrorContext; -import org.apache.ibatis.executor.loader.ProxyFactory; -import org.apache.ibatis.io.Resources; -import org.apache.ibatis.mapping.DatabaseIdProvider; -import org.apache.ibatis.mapping.Environment; -import org.apache.ibatis.parsing.XNode; -import org.apache.ibatis.parsing.XPathParser; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.reflection.DefaultReflectorFactory; -import org.apache.ibatis.reflection.MetaClass; -import org.apache.ibatis.reflection.ReflectorFactory; -import org.apache.ibatis.reflection.factory.ObjectFactory; -import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; -import org.apache.ibatis.session.AutoMappingBehavior; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.ExecutorType; -import org.apache.ibatis.session.LocalCacheScope; -import org.apache.ibatis.transaction.TransactionFactory; -import org.apache.ibatis.type.JdbcType; -import org.springframework.core.io.Resource; - - -/** - * Extends the MyBatis XMLConfigBuilder to allow the selection of a {@link org.springframework.core.io.ResourceLoader} - * that will be used to load the resources specified in the mapper's resource. - *

- * By using the resource.dialect placeholder with hierarchical resource loading, - * different resource files can be picked up for different dialects. This reduces duplication - * when supporting multiple database configurations. - *

- * <configuration>
- *    <mappers>
- *        <mapper resource="org/x/y/#resource.dialect#/View1.xml"/>
- *        <mapper resource="org/x/y/#resource.dialect#/View2.xml"/>
- *    </mappers>
- * </configuration>
- * 

- * - * Much of the implementation is a direct copy of the MyBatis {@link org.apache.ibatis.builder.xml.XMLConfigBuilder}; some - * of the protected methods do not have access to the object's state and can therefore - * not be overridden successfully: IBATIS-589 - - * Pending a better way to extend/override, much of the implementation is a direct copy of the MyBatis - * {@link org.mybatis.spring.SqlSessionFactoryBean}; some of the protected methods do not have access to the object's state - * and can therefore not be overridden successfully. - * - * This is equivalent to HierarchicalSqlMapConfigParser which extended iBatis (2.x). - * See also: IBATIS-589 - * and: - * - * @author Derek Hulley, janv - * @since 4.0 - */ -// note: effectively extends XMLConfigBuilder to use hierarchical resource loader -public class HierarchicalXMLConfigBuilder extends BaseBuilder -{ - private boolean parsed; - private XPathParser parser; - private String environment; - private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); - - // EXTENDED - final private HierarchicalResourceLoader resourceLoader; - - public HierarchicalXMLConfigBuilder(HierarchicalResourceLoader resourceLoader, InputStream inputStream, String environment, Properties props) - { - super(new Configuration()); - - // EXTENDED - this.resourceLoader = resourceLoader; - - ErrorContext.instance().resource("SQL Mapper Configuration"); - this.configuration.setVariables(props); - this.parsed = false; - this.environment = environment; - this.parser = new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()); - } - - public Configuration parse() { - if (parsed) { - throw new BuilderException("Each XMLConfigBuilder can only be used once."); - } - parsed = true; - parseConfiguration(parser.evalNode("/configuration")); - return configuration; - } - - private void parseConfiguration(XNode root) { - try { - //issue #117 read properties first - propertiesElement(root.evalNode("properties")); - typeAliasesElement(root.evalNode("typeAliases")); - pluginElement(root.evalNode("plugins")); - objectFactoryElement(root.evalNode("objectFactory")); - objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); - reflectionFactoryElement(root.evalNode("reflectionFactory")); - settingsElement(root.evalNode("settings")); - // read it after objectFactory and objectWrapperFactory issue #631 - environmentsElement(root.evalNode("environments")); - databaseIdProviderElement(root.evalNode("databaseIdProvider")); - typeHandlerElement(root.evalNode("typeHandlers")); - mapperElement(root.evalNode("mappers")); - } catch (Exception e) { - throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); - } - } - - private void typeAliasesElement(XNode parent) { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String typeAliasPackage = child.getStringAttribute("name"); - configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); - } else { - String alias = child.getStringAttribute("alias"); - String type = child.getStringAttribute("type"); - try { - Class clazz = Resources.classForName(type); - if (alias == null) { - typeAliasRegistry.registerAlias(clazz); - } else { - typeAliasRegistry.registerAlias(alias, clazz); - } - } catch (ClassNotFoundException e) { - throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); - } - } - } - } - } - - private void pluginElement(XNode parent) throws Exception { - if (parent != null) { - for (XNode child : parent.getChildren()) { - String interceptor = child.getStringAttribute("interceptor"); - Properties properties = child.getChildrenAsProperties(); - Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); - interceptorInstance.setProperties(properties); - configuration.addInterceptor(interceptorInstance); - } - } - } - - private void objectFactoryElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - Properties properties = context.getChildrenAsProperties(); - ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance(); - factory.setProperties(properties); - configuration.setObjectFactory(factory); - } - } - - private void objectWrapperFactoryElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance(); - configuration.setObjectWrapperFactory(factory); - } - } - - private void reflectionFactoryElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance(); - configuration.setReflectorFactory(factory); - } - } - - private void propertiesElement(XNode context) throws Exception { - if (context != null) { - Properties defaults = context.getChildrenAsProperties(); - String resource = context.getStringAttribute("resource"); - String url = context.getStringAttribute("url"); - if (resource != null && url != null) { - throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); - } - if (resource != null) { - defaults.putAll(Resources.getResourceAsProperties(resource)); - } else if (url != null) { - defaults.putAll(Resources.getUrlAsProperties(url)); - } - Properties vars = configuration.getVariables(); - if (vars != null) { - defaults.putAll(vars); - } - parser.setVariables(defaults); - configuration.setVariables(defaults); - } - } - - private void settingsElement(XNode context) throws Exception { - if (context != null) { - Properties props = context.getChildrenAsProperties(); - // Check that all settings are known to the configuration class - MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); - for (Object key : props.keySet()) { - if (!metaConfig.hasSetter(String.valueOf(key))) { - throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); - } - } - configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); - configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); - configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); - configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); - configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true)); - configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); - configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); - configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); - configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); - configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); - configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); - configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); - configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); - configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); - configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); - configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); - configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); - configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); - configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); - configuration.setLogPrefix(props.getProperty("logPrefix")); - configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); - } - } - - private void environmentsElement(XNode context) throws Exception { - if (context != null) { - if (environment == null) { - environment = context.getStringAttribute("default"); - } - for (XNode child : context.getChildren()) { - String id = child.getStringAttribute("id"); - if (isSpecifiedEnvironment(id)) { - TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); - DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); - DataSource dataSource = dsFactory.getDataSource(); - Environment.Builder environmentBuilder = new Environment.Builder(id) - .transactionFactory(txFactory) - .dataSource(dataSource); - configuration.setEnvironment(environmentBuilder.build()); - } - } - } - } - - private void databaseIdProviderElement(XNode context) throws Exception { - DatabaseIdProvider databaseIdProvider = null; - if (context != null) { - String type = context.getStringAttribute("type"); - Properties properties = context.getChildrenAsProperties(); - databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance(); - databaseIdProvider.setProperties(properties); - } - Environment environment = configuration.getEnvironment(); - if (environment != null && databaseIdProvider != null) { - String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); - configuration.setDatabaseId(databaseId); - } - } - - private TransactionFactory transactionManagerElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - Properties props = context.getChildrenAsProperties(); - TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); - factory.setProperties(props); - return factory; - } - throw new BuilderException("Environment declaration requires a TransactionFactory."); - } - - private DataSourceFactory dataSourceElement(XNode context) throws Exception { - if (context != null) { - String type = context.getStringAttribute("type"); - Properties props = context.getChildrenAsProperties(); - DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); - factory.setProperties(props); - return factory; - } - throw new BuilderException("Environment declaration requires a DataSourceFactory."); - } - - private void typeHandlerElement(XNode parent) throws Exception { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String typeHandlerPackage = child.getStringAttribute("name"); - typeHandlerRegistry.register(typeHandlerPackage); - } else { - String javaTypeName = child.getStringAttribute("javaType"); - String jdbcTypeName = child.getStringAttribute("jdbcType"); - String handlerTypeName = child.getStringAttribute("handler"); - Class javaTypeClass = resolveClass(javaTypeName); - JdbcType jdbcType = resolveJdbcType(jdbcTypeName); - Class typeHandlerClass = resolveClass(handlerTypeName); - if (javaTypeClass != null) { - if (jdbcType == null) { - typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); - } else { - typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); - } - } else { - typeHandlerRegistry.register(typeHandlerClass); - } - } - } - } - } - - private void mapperElement(XNode parent) throws Exception { - if (parent != null) { - for (XNode child : parent.getChildren()) { - if ("package".equals(child.getName())) { - String mapperPackage = child.getStringAttribute("name"); - configuration.addMappers(mapperPackage); - } else { - String resource = child.getStringAttribute("resource"); - String url = child.getStringAttribute("url"); - String mapperClass = child.getStringAttribute("class"); - if (resource != null && url == null && mapperClass == null) { - ErrorContext.instance().resource(resource); - - // // EXTENDED - // inputStream = Resources.getResourceAsStream(resource); - InputStream inputStream = null; - Resource res = resourceLoader.getResource(resource); - if (res != null && res.exists()) - { - inputStream = res.getInputStream(); - } - else { - throw new BuilderException("Failed to get resource: "+resource); - } - - //InputStream inputStream = Resources.getResourceAsStream(resource); - XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); - mapperParser.parse(); - } else if (resource == null && url != null && mapperClass == null) { - ErrorContext.instance().resource(url); - InputStream inputStream = Resources.getUrlAsStream(url); - XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); - mapperParser.parse(); - } else if (resource == null && url == null && mapperClass != null) { - Class mapperInterface = Resources.classForName(mapperClass); - configuration.addMapper(mapperInterface); - } else { - throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); - } - } - } - } - } - - private boolean isSpecifiedEnvironment(String id) { - if (environment == null) { - throw new BuilderException("No environment specified."); - } else if (id == null) { - throw new BuilderException("Environment requires an id attribute."); - } else if (environment.equals(id)) { - return true; - } - return false; - } -} diff --git a/src/main/java/org/alfresco/ibatis/IdsEntity.java b/src/main/java/org/alfresco/ibatis/IdsEntity.java deleted file mode 100644 index d8f4f09e2c..0000000000 --- a/src/main/java/org/alfresco/ibatis/IdsEntity.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2005-2013 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import java.util.List; - -/** - * Entity bean to carry ID-style information - * - * @author Derek Hulley - * @since 3.2 - */ -public class IdsEntity -{ - private Long idOne; - private Long idTwo; - private Long idThree; - private Long idFour; - private List ids; - public Long getIdOne() - { - return idOne; - } - public void setIdOne(Long id) - { - this.idOne = id; - } - public Long getIdTwo() - { - return idTwo; - } - public void setIdTwo(Long id) - { - this.idTwo = id; - } - public Long getIdThree() - { - return idThree; - } - public void setIdThree(Long idThree) - { - this.idThree = idThree; - } - public Long getIdFour() - { - return idFour; - } - public void setIdFour(Long idFour) - { - this.idFour = idFour; - } - public List getIds() - { - return ids; - } - public void setIds(List ids) - { - this.ids = ids; - } -} diff --git a/src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java b/src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java deleted file mode 100644 index 6b92b5bf6c..0000000000 --- a/src/main/java/org/alfresco/ibatis/RetryingCallbackHelper.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * A helper that runs a unit of work, transparently retrying the unit of work if - * an error occurs. - *

- * Defaults: - *

- * - * @author Derek Hulley - * @since 3.4 - */ -public class RetryingCallbackHelper -{ - private static final Log logger = LogFactory.getLog(RetryingCallbackHelper.class); - - /** The maximum number of retries. -1 for infinity. */ - private int maxRetries; - /** How much time to wait with each retry. */ - private int retryWaitMs; - - /** - * Callback interface - * @author Derek Hulley - */ - public interface RetryingCallback - { - /** - * Perform a unit of work. - * - * @return Return the result of the unit of work - * @throws Throwable This can be anything and will guarantee either a retry or a rollback - */ - public Result execute() throws Throwable; - }; - - /** - * Default constructor. - */ - public RetryingCallbackHelper() - { - this.maxRetries = 5; - this.retryWaitMs = 10; - } - - /** - * Set the maximimum number of retries. -1 for infinity. - */ - public void setMaxRetries(int maxRetries) - { - this.maxRetries = maxRetries; - } - - public void setRetryWaitMs(int retryWaitMs) - { - this.retryWaitMs = retryWaitMs; - } - - /** - * Execute a callback until it succeeds, fails or until a maximum number of retries have - * been attempted. - * - * @param callback The callback containing the unit of work. - * @return Returns the result of the unit of work. - * @throws RuntimeException all checked exceptions are converted - */ - public R doWithRetry(RetryingCallback callback) - { - // Track the last exception caught, so that we can throw it if we run out of retries. - RuntimeException lastException = null; - for (int count = 0; count == 0 || count < maxRetries; count++) - { - try - { - // Do the work. - R result = callback.execute(); - if (logger.isDebugEnabled()) - { - if (count != 0) - { - logger.debug("\n" + - "Retrying work succeeded: \n" + - " Thread: " + Thread.currentThread().getName() + "\n" + - " Iteration: " + count); - } - } - return result; - } - catch (Throwable e) - { - lastException = (e instanceof RuntimeException) ? - (RuntimeException) e : - new AlfrescoRuntimeException("Exception in Transaction.", e); - if (logger.isDebugEnabled()) - { - logger.debug("\n" + - "Retrying work failed: \n" + - " Thread: " + Thread.currentThread().getName() + "\n" + - " Iteration: " + count + "\n" + - " Exception follows:", - e); - } - else if (logger.isInfoEnabled()) - { - String msg = String.format( - "Retrying %s: count %2d; wait: %3dms; msg: \"%s\"; exception: (%s)", - Thread.currentThread().getName(), - count, retryWaitMs, - e.getMessage(), - e.getClass().getName()); - logger.info(msg); - } - try - { - Thread.sleep(retryWaitMs); - } - catch (InterruptedException ie) - { - // Do nothing. - } - // Try again - continue; - } - } - // We've worn out our welcome and retried the maximum number of times. - // So, fail. - throw lastException; - } -} diff --git a/src/main/java/org/alfresco/ibatis/RollupResultHandler.java b/src/main/java/org/alfresco/ibatis/RollupResultHandler.java deleted file mode 100644 index 4d2419e46a..0000000000 --- a/src/main/java/org/alfresco/ibatis/RollupResultHandler.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import org.apache.ibatis.executor.result.DefaultResultContext; -import org.apache.ibatis.reflection.MetaObject; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.ResultContext; -import org.apache.ibatis.session.ResultHandler; -import org.mybatis.spring.SqlSessionTemplate; - -/** - * A {@link ResultHandler} that collapses multiple rows based on a set of properties. - *

- * This class is derived from earlier RollupRowHandler used to workaround the groupBy and nested ResultMap - * behaviour in iBatis (2.3.4.726) IBATIS-503. - *

- * The set of properties given act as a unique key. When the unique key changes, the collection - * values from the nested ResultMap are coalesced and the given {@link ResultHandler} is called. It is - * possible to embed several instances of this handler for deeply-nested ResultMap declarations. - *

- * Use this instance as a regular {@link ResultHandler}, but with one big exception: call {@link #processLastResults()} - * after executing the SQL statement. Remove the groupBy attribute from the iBatis ResultMap - * declaration. - *

- * Example iBatis 2.x (TODO migrate example to MyBatis 3.x): - *

-    <resultMap id="result_AuditQueryAllValues"
-               extends="alfresco.audit.result_AuditQueryNoValues"
-               class="AuditQueryResult">
-        <result property="auditValues" resultMap="alfresco.propval.result_PropertyIdSearchRow"/>
-    </resultMap>
- * 
- * Example usage: - *
-        RowHandler rowHandler = new RowHandler()
-        {
-            public void handleRow(Object valueObject)
-            {
-                // DO SOMETHING
-            }
-        };
-        RollupRowHandler rollupRowHandler = new RollupRowHandler(
-                new String[] {"auditEntryId"},
-                "auditValues",
-                rowHandler,
-                maxResults);
-        
-        if (maxResults > 0)
-        {
-            // Calculate the maximum results required
-            int sqlMaxResults = (maxResults > 0 ? ((maxResults+1) * 20) : Integer.MAX_VALUE);
-            
-            List rows = template.queryForList(SELECT_ENTRIES_WITH_VALUES, params, 0, sqlMaxResults);
-            for (AuditQueryResult row : rows)
-            {
-                rollupRowHandler.handleRow(row);
-            }
-            // Don't process last result:
-            //    rollupRowHandler.processLastResults();
-            //    The last result may be incomplete
-        }
-        else
-        {
-            template.queryWithRowHandler(SELECT_ENTRIES_WITH_VALUES, params, rollupRowHandler);
-            rollupRowHandler.processLastResults();
-        }
- * 
- *

- * This class is not thread-safe; use a new instance for each use. - * - * @author Derek Hulley, janv - * @since 4.0 - */ -public class RollupResultHandler implements ResultHandler -{ - private final String[] keyProperties; - private final String collectionProperty; - private final ResultHandler resultHandler; - private final int maxResults; - - private Object[] lastKeyValues; - private List rawResults; - private int resultCount; - - private Configuration configuration; - - /** - * @param keyProperties the properties that make up the unique key - * @param collectionProperty the property mapped using a nested ResultMap - * @param resultHandler the result handler that will receive the rolled-up results - */ - public RollupResultHandler(Configuration configuration, String[] keyProperties, String collectionProperty, ResultHandler resultHandler) - { - this(configuration, keyProperties, collectionProperty, resultHandler, Integer.MAX_VALUE); - } - - /** - * @param keyProperties the properties that make up the unique key - * @param collectionProperty the property mapped using a nested ResultMap - * @param resultHandler the result handler that will receive the rolled-up results - * @param maxResults the maximum number of results to retrieve (-1 for no limit). - * Make sure that the query result limit is large enough to produce this - * at least this number of results - */ - public RollupResultHandler(Configuration configuration, String[] keyProperties, String collectionProperty, ResultHandler resultHandler, int maxResults) - { - if (keyProperties == null || keyProperties.length == 0) - { - throw new IllegalArgumentException("RollupRowHandler can only be used with at least one key property."); - } - if (collectionProperty == null) - { - throw new IllegalArgumentException("RollupRowHandler must have a collection property."); - } - this.configuration = configuration; - this.keyProperties = keyProperties; - this.collectionProperty = collectionProperty; - this.resultHandler = resultHandler; - this.maxResults = maxResults; - this.rawResults = new ArrayList(100); - } - - public void handleResult(ResultContext context) - { - // Shortcut if we have processed enough results - if (maxResults > 0 && resultCount >= maxResults) - { - return; - } - - Object valueObject = context.getResultObject(); - MetaObject probe = configuration.newMetaObject(valueObject); - - // Check if the key has changed - if (lastKeyValues == null) - { - lastKeyValues = getKeyValues(probe); - resultCount = 0; - } - // Check if it has changed - Object[] currentKeyValues = getKeyValues(probe); - if (!Arrays.deepEquals(lastKeyValues, currentKeyValues)) - { - // Key has changed, so handle the results - Object resultObject = coalesceResults(configuration, rawResults, collectionProperty); - if (resultObject != null) - { - DefaultResultContext resultContext = new DefaultResultContext(); - resultContext.nextResultObject(resultObject); - - resultHandler.handleResult(resultContext); - resultCount++; - } - rawResults.clear(); - lastKeyValues = currentKeyValues; - } - // Add the new value to the results for next time - rawResults.add(valueObject); - // Done - } - - /** - * Client code must call this method once the query returns so that the final results - * can be passed to the inner RowHandler. If a query is limited by size, then it is - * possible that the unprocessed results represent an incomplete final object; in this case - * it would be best to ignore the last results. If the query is complete (i.e. all results - * are returned) then this method should be called. - *

- * If you want X results and each result is made up of N rows (on average), then set the query - * limit to:
- * L = X * (N+1)
- * and don't call this method. - */ - public void processLastResults() - { - // Shortcut if we have processed enough results - if (maxResults > 0 && resultCount >= maxResults) - { - return; - } - // Handle any outstanding results - Object resultObject = coalesceResults(configuration, rawResults, collectionProperty); - if (resultObject != null) - { - DefaultResultContext resultContext = new DefaultResultContext(); - resultContext.nextResultObject(resultObject); - - resultHandler.handleResult(resultContext); - resultCount++; - rawResults.clear(); // Stop it from being used again - } - } - - @SuppressWarnings("unchecked") - private static Object coalesceResults(Configuration configuration, List valueObjects, String collectionProperty) - { - // Take the first result as the base value - Object resultObject = null; - MetaObject probe = null; - Collection collection = null; - for (Object object : valueObjects) - { - if (collection == null) - { - resultObject = object; - probe = configuration.newMetaObject(resultObject); - collection = (Collection) probe.getValue(collectionProperty); - } - else - { - Collection addedValues = (Collection) probe.getValue(collectionProperty); - collection.addAll(addedValues); - } - } - // Done - return resultObject; - } - - /** - * @return Returns the values for the {@link RollupResultHandler#keyProperties} - */ - private Object[] getKeyValues(MetaObject probe) - { - Object[] keyValues = new Object[keyProperties.length]; - for (int i = 0; i < keyProperties.length; i++) - { - keyValues[i] = probe.getValue(keyProperties[i]); - } - return keyValues; - } -} diff --git a/src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java b/src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java deleted file mode 100644 index 094264cae2..0000000000 --- a/src/main/java/org/alfresco/ibatis/SerializableTypeHandler.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; - -import org.apache.ibatis.type.JdbcType; -import org.apache.ibatis.type.TypeHandler; - -/** - * MyBatis 3.x TypeHandler for java.io.Serializable to BLOB types. - * - * @author Derek Hulley, janv - * @since 4.0 - */ -public class SerializableTypeHandler implements TypeHandler -{ - public static final int DEFAULT_SERIALIZABLE_TYPE = Types.LONGVARBINARY; - private static volatile int serializableType = DEFAULT_SERIALIZABLE_TYPE; - - /** - * @see Types - */ - public static void setSerializableType(int serializableType) - { - SerializableTypeHandler.serializableType = serializableType; - } - - /** - * @return Returns the SQL type to use for serializable columns - */ - public static int getSerializableType() - { - return serializableType; - } - - /** - * @throws DeserializationException if the object could not be deserialized - */ - public Object getResult(ResultSet rs, String columnName) throws SQLException - { - final Serializable ret; - try - { - InputStream is = rs.getBinaryStream(columnName); - if (is == null || rs.wasNull()) - { - return null; - } - // Get the stream and deserialize - ObjectInputStream ois = new ObjectInputStream(is); - Object obj = ois.readObject(); - // Success - ret = (Serializable) obj; - } - catch (Throwable e) - { - throw new DeserializationException(e); - } - return ret; - } - - @Override - public Object getResult(ResultSet rs, int columnIndex) throws SQLException - { - final Serializable ret; - try - { - InputStream is = rs.getBinaryStream(columnIndex); - if (is == null || rs.wasNull()) - { - return null; - } - // Get the stream and deserialize - ObjectInputStream ois = new ObjectInputStream(is); - Object obj = ois.readObject(); - // Success - ret = (Serializable) obj; - } - catch (Throwable e) - { - throw new DeserializationException(e); - } - return ret; - } - - public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException - { - if (parameter == null) - { - ps.setNull(i, SerializableTypeHandler.serializableType); - } - else - { - try - { - ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(parameter); - byte[] bytes = baos.toByteArray(); - ByteArrayInputStream bais = new ByteArrayInputStream(bytes); - ps.setBinaryStream(i, bais, bytes.length); - } - catch (Throwable e) - { - throw new SerializationException(e); - } - } - } - - public Object getResult(CallableStatement cs, int columnIndex) throws SQLException - { - throw new UnsupportedOperationException("Unsupported"); - } - - /** - * @return Returns the value given - */ - public Object valueOf(String s) - { - return s; - } - - /** - * Marker exception to allow deserialization issues to be dealt with by calling code. - * If this exception remains uncaught, it will be very difficult to find and rectify - * the data issue. - * - * @author Derek Hulley - * @since 3.2 - */ - public static class DeserializationException extends RuntimeException - { - private static final long serialVersionUID = 4673487701048985340L; - - public DeserializationException(Throwable cause) - { - super(cause); - } - } - - /** - * Marker exception to allow serialization issues to be dealt with by calling code. - * Unlike with {@link DeserializationException deserialization}, it is not important - * to handle this exception neatly. - * - * @author Derek Hulley - * @since 3.2 - */ - public static class SerializationException extends RuntimeException - { - private static final long serialVersionUID = 962957884262870228L; - - public SerializationException(Throwable cause) - { - super(cause); - } - } -} diff --git a/src/main/java/org/alfresco/util/AbstractTriggerBean.java b/src/main/java/org/alfresco/util/AbstractTriggerBean.java deleted file mode 100644 index 079374693e..0000000000 --- a/src/main/java/org/alfresco/util/AbstractTriggerBean.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2005-2014 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.util; - -import org.alfresco.api.AlfrescoPublicApi; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.util.bean.BooleanBean; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.quartz.JobDetail; -import org.quartz.Scheduler; -import org.quartz.Trigger; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.scheduling.quartz.JobDetailAwareTrigger; - -/** - * A utility bean to wrap sceduling a job with a scheduler. - * - * @author Andy Hind - */ -@AlfrescoPublicApi -public abstract class AbstractTriggerBean implements InitializingBean, JobDetailAwareTrigger, BeanNameAware, DisposableBean -{ - - protected static Log logger = LogFactory.getLog(AbstractTriggerBean.class); - - private JobDetail jobDetail; - - private Scheduler scheduler; - - private String beanName; - - private Trigger trigger; - - private boolean enabled = true; - - public AbstractTriggerBean() - { - super(); - } - - /** - * Get the definition of the job to run. - */ - public JobDetail getJobDetail() - { - return jobDetail; - } - - /** - * Set the definition of the job to run. - * - * @param jobDetail - */ - public void setJobDetail(JobDetail jobDetail) - { - this.jobDetail = jobDetail; - } - - /** - * Get the scheduler with which the job and trigger are scheduled. - * - * @return The scheduler - */ - public Scheduler getScheduler() - { - return scheduler; - } - - /** - * Set the scheduler. - * - * @param scheduler - */ - public void setScheduler(Scheduler scheduler) - { - this.scheduler = scheduler; - } - - /** - * Set the scheduler - */ - public void afterPropertiesSet() throws Exception - { - // Check properties are set - if (jobDetail == null) - { - throw new AlfrescoRuntimeException("Job detail has not been set"); - } - if (scheduler == null) - { - logger.warn("Job " + getBeanName() + " is not active"); - } - else if (!enabled) - { - logger.warn("Job " + getBeanName() + " is not enabled"); - } - else - { - logger.info("Job " + getBeanName() + " is active and enabled"); - // Register the job with the scheduler - this.trigger = getTrigger(); - if (this.trigger == null) - { - logger.error("Job " + getBeanName() + " is not active (invalid trigger)"); - } - else - { - logger.info("Job " + getBeanName() + " is active"); - - String jobName = jobDetail.getKey().getName(); - String groupName = jobDetail.getKey().getGroup(); - - if(scheduler.getJobDetail(jobName, groupName) != null) - { - // Job is already defined delete it - if(logger.isDebugEnabled()) - { - logger.debug("job already registered with scheduler jobName:" + jobName); - } - scheduler.deleteJob(jobName, groupName); - } - - if(logger.isDebugEnabled()) - { - logger.debug("schedule job:" + jobDetail + " using " + this.trigger - + " startTime: " + this.trigger.getStartTime()); - } - scheduler.scheduleJob(jobDetail, this.trigger); - } - } - } - - /** - * Ensures that the job is unscheduled with the context is shut down. - */ - public void destroy() throws Exception - { - if (this.trigger != null) - { - if (!this.scheduler.isShutdown()) - { - scheduler.unscheduleJob(this.trigger.getName(), this.trigger.getGroup()); - } - this.trigger = null; - } - } - - /** - * Abstract method for implementations to build their trigger. - * - * @return The trigger - * @throws Exception - */ - public abstract Trigger getTrigger() throws Exception; - - /** - * Get the bean name as this trigger is created - */ - public void setBeanName(String name) - { - this.beanName = name; - } - - /** - * Get the bean/trigger name. - * - * @return The name of the bean - */ - public String getBeanName() - { - return beanName; - } - - - public boolean isEnabled() - { - return enabled; - } - - public void setEnabled(boolean enabled) - { - this.enabled = enabled; - } - - public void setEnabledFromBean(BooleanBean enabled) - { - this.enabled = enabled.isTrue(); - } -} diff --git a/src/main/java/org/alfresco/util/CronTriggerBean.java b/src/main/java/org/alfresco/util/CronTriggerBean.java deleted file mode 100644 index 25fe78c78c..0000000000 --- a/src/main/java/org/alfresco/util/CronTriggerBean.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2005-2014 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.util; - -import java.util.Date; - -import org.alfresco.api.AlfrescoPublicApi; -import org.alfresco.error.AlfrescoRuntimeException; -import org.quartz.CronTrigger; -import org.quartz.JobDetail; -import org.quartz.Scheduler; -import org.quartz.Trigger; - -/** - * A utility bean to wrap scheduling a cron job with a given scheduler. - * - * @author Andy Hind - */ -@AlfrescoPublicApi -public class CronTriggerBean extends AbstractTriggerBean -{ - private static final long MILLISECONDS_PER_MINUTE = 60L * 1000L; - - /* - * Milliseconds delay before the job will be triggered. - */ - private long startDelay = 0; - - /* - * The cron expression to trigger execution. - */ - String cronExpression; - - /** - * Default constructor - * - */ - public CronTriggerBean() - { - super(); - } - - /** - * Get the cron expression that determines when this job is run. - * - * @return The cron expression - */ - public String getCronExpression() - { - return cronExpression; - } - - /** - * Set the cron expression that determines when this job is run. - * - * @param cronExpression - */ - public void setCronExpression(String cronExpression) - { - this.cronExpression = cronExpression; - } - - /** - * Build the cron trigger - * - * @return The trigger - * @throws Exception - */ - public Trigger getTrigger() throws Exception - { - Trigger trigger = new CronTrigger(getBeanName(), Scheduler.DEFAULT_GROUP, getCronExpression()); - if (this.startDelay > 0) - { - trigger.setStartTime(new Date(System.currentTimeMillis() + this.startDelay)); - } - JobDetail jd = super.getJobDetail(); - if (jd != null) - { - String jobName = super.getJobDetail().getKey().getName(); - if (jobName != null && !jobName.isEmpty()) - { - trigger.setJobName(jobName); - } - String jobGroup = super.getJobDetail().getKey().getGroup(); - if (jobGroup != null && !jobGroup.isEmpty()) - { - trigger.setJobGroup(jobGroup); - } - } - return trigger; - } - - public long getStartDelay() - { - return startDelay; - } - - public void setStartDelay(long startDelay) - { - this.startDelay = startDelay; - } - - public void setStartDelayMinutes(long startDelayMinutes) - { - this.startDelay = startDelayMinutes * MILLISECONDS_PER_MINUTE; - } - - - public void afterPropertiesSet() throws Exception - { - if ((cronExpression == null) || (cronExpression.trim().length() == 0)) - { - throw new AlfrescoRuntimeException( - "The cron expression has not been set, is zero length, or is all white space"); - } - super.afterPropertiesSet(); - } -} diff --git a/src/main/java/org/alfresco/util/TriggerBean.java b/src/main/java/org/alfresco/util/TriggerBean.java deleted file mode 100644 index 620e6a2aee..0000000000 --- a/src/main/java/org/alfresco/util/TriggerBean.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.util; - -import java.util.Date; - -import org.quartz.Scheduler; -import org.quartz.SimpleTrigger; -import org.quartz.Trigger; - -public class TriggerBean extends AbstractTriggerBean implements TriggerBeanSPI -{ - public long startDelay = 0; - - public long repeatInterval = 0; - - public int repeatCount = SimpleTrigger.REPEAT_INDEFINITELY; - - public TriggerBean() - { - super(); - } - - @Override - public int getRepeatCount() - { - return repeatCount; - } - - @Override - public void setRepeatCount(int repeatCount) - { - this.repeatCount = repeatCount; - } - - @Override - public long getRepeatInterval() - { - return repeatInterval; - } - - @Override - public void setRepeatInterval(long repeatInterval) - { - this.repeatInterval = repeatInterval; - } - - @Override - public void setRepeatIntervalMinutes(long repeatIntervalMinutes) - { - this.repeatInterval = repeatIntervalMinutes * 60L * 1000L; - } - - @Override - public long getStartDelay() - { - return startDelay; - } - - @Override - public void setStartDelay(long startDelay) - { - this.startDelay = startDelay; - } - - @Override - public void setStartDelayMinutes(long startDelayMinutes) - { - this.startDelay = startDelayMinutes * 60L * 1000L; - } - - @Override - public Trigger getTrigger() throws Exception - { - if ((repeatInterval <= 0) && (repeatCount != 0)) - { - logger.error("Job "+getBeanName()+" - repeatInterval/repeatIntervalMinutes cannot be 0 (or -ve) unless repeatCount is also 0"); - return null; - } - - SimpleTrigger trigger = new SimpleTrigger(getBeanName(), Scheduler.DEFAULT_GROUP); - trigger.setStartTime(new Date(System.currentTimeMillis() + this.startDelay)); - trigger.setRepeatCount(repeatCount); - trigger.setRepeatInterval(repeatInterval); - return trigger; - } -} diff --git a/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java b/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java index 0c6c076786..9f3749af32 100644 --- a/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java +++ b/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java @@ -33,6 +33,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; +import org.springframework.transaction.support.TransactionSynchronizationManager; /** * The base implementation for an asynchronously refreshed cache. @@ -280,7 +281,12 @@ public abstract class AbstractAsynchronouslyRefreshedCache // ensure that we get the transaction callbacks as we have bound the unique // transactional caches to a common manager - TransactionSupportUtil.bindListener(this, 0); + // The synchronizations are not available after the txn is committed/rolled back + // the resources are still stored in org.alfresco.util.transaction.TransactionSupportUtil + if (TransactionSynchronizationManager.isSynchronizationActive()) + { + TransactionSupportUtil.bindListener(this, 0); + } TransactionSupportUtil.bindResource(resourceKeyTxnData, data); } return data; diff --git a/src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java b/src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java deleted file mode 100644 index 87d845867f..0000000000 --- a/src/main/java/org/alfresco/util/resource/HierarchicalResourceLoader.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.util.resource; - -import org.alfresco.util.PropertyCheck; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; - -/** - * Locate resources by using a class hierarchy to drive the search. The well-known - * placeholder {@link #DEFAULT_DIALECT_PLACEHOLDER} is replaced with successive class - * names starting from the {@link #setDialectClass(String) dialect class} and - * progressing up the hierarchy until the {@link #setDialectBaseClass(String) base class} - * is reached. A full resource search using Spring's {@link DefaultResourceLoader} is - * done at each point until the resource is found or the base of the class hierarchy is - * reached. - *

- * For example assume classpath resources:
- *

- *    RESOURCE 1: config/ibatis/org.hibernate.dialect.Dialect/SqlMap-DOG.xml
- *    RESOURCE 2: config/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/SqlMap-DOG.xml
- *    RESOURCE 3: config/ibatis/org.hibernate.dialect.Dialect/SqlMap-CAT.xml
- *    RESOURCE 4: config/ibatis/org.hibernate.dialect.MySQLDialect/SqlMap-CAT.xml
- * 
- * and
- *
- *    dialectBaseClass = org.hibernate.dialect.Dialect
- * 
- * For dialect org.hibernate.dialect.MySQLInnoDBDialect the following will be returned:
- *
- *    config/ibatis/#resource.dialect#/SqlMap-DOG.xml == RESOURCE 2
- *    config/ibatis/#resource.dialect#/SqlMap-CAT.xml == RESOURCE 4
- * 
- * For dialectorg.hibernate.dialect.MySQLDBDialect the following will be returned:
- *
- *    config/ibatis/#resource.dialect#/SqlMap-DOG.xml == RESOURCE 1
- *    config/ibatis/#resource.dialect#/SqlMap-CAT.xml == RESOURCE 4
- * 
- * For dialectorg.hibernate.dialect.Dialect the following will be returned:
- *
- *    config/ibatis/#resource.dialect#/SqlMap-DOG.xml == RESOURCE 1
- *    config/ibatis/#resource.dialect#/SqlMap-CAT.xml == RESOURCE 3
- * 
- * - * @author Derek Hulley - * @since 3.2 (Mobile) - */ -public class HierarchicalResourceLoader extends DefaultResourceLoader implements InitializingBean -{ - public static final String DEFAULT_DIALECT_PLACEHOLDER = "#resource.dialect#"; - public static final String DEFAULT_DIALECT_REGEX = "\\#resource\\.dialect\\#"; - - private String dialectBaseClass; - private String dialectClass; - - /** - * Create a new HierarchicalResourceLoader. - */ - public HierarchicalResourceLoader() - { - super(); - } - - /** - * Set the class to be used during hierarchical dialect replacement. Searches for the - * configuration location will not go further up the hierarchy than this class. - * - * @param className the name of the class or interface - */ - public void setDialectBaseClass(String className) - { - this.dialectBaseClass = className; - } - - public void setDialectClass(String className) - { - this.dialectClass = className; - } - - public void afterPropertiesSet() throws Exception - { - PropertyCheck.mandatory(this, "dialectBaseClass", dialectBaseClass); - PropertyCheck.mandatory(this, "dialectClass", dialectClass); - } - - /** - * Get a resource using the defined class hierarchy as a search path. - * - * @param location the location including a {@link #DEFAULT_DIALECT_PLACEHOLDER placeholder} - * @return a resource found by successive searches using class name replacement, or - * null if not found. - */ - @SuppressWarnings("unchecked") - @Override - public Resource getResource(String location) - { - if (dialectClass == null || dialectBaseClass == null) - { - return super.getResource(location); - } - - // If a property value has not been substituted, extract the property name and load from system - String dialectBaseClassStr = dialectBaseClass; - if (!PropertyCheck.isValidPropertyString(dialectBaseClass)) - { - String prop = PropertyCheck.getPropertyName(dialectBaseClass); - dialectBaseClassStr = System.getProperty(prop, dialectBaseClass); - } - String dialectClassStr = dialectClass; - if (!PropertyCheck.isValidPropertyString(dialectClass)) - { - String prop = PropertyCheck.getPropertyName(dialectClass); - dialectClassStr = System.getProperty(prop, dialectClass); - } - - Class dialectBaseClazz; - try - { - dialectBaseClazz = Class.forName(dialectBaseClassStr); - } - catch (ClassNotFoundException e) - { - throw new RuntimeException("Dialect base class not found: " + dialectBaseClassStr); - } - Class dialectClazz; - try - { - dialectClazz = Class.forName(dialectClassStr); - } - catch (ClassNotFoundException e) - { - throw new RuntimeException("Dialect class not found: " + dialectClassStr); - } - // Ensure that we are dealing with classes and not interfaces - if (!Object.class.isAssignableFrom(dialectBaseClazz)) - { - throw new RuntimeException( - "Dialect base class must be derived from java.lang.Object: " + - dialectBaseClazz.getName()); - } - if (!Object.class.isAssignableFrom(dialectClazz)) - { - throw new RuntimeException( - "Dialect class must be derived from java.lang.Object: " + - dialectClazz.getName()); - } - // We expect these to be in the same hierarchy - if (!dialectBaseClazz.isAssignableFrom(dialectClazz)) - { - throw new RuntimeException( - "Non-existent HierarchicalResourceLoader hierarchy: " + - dialectBaseClazz.getName() + " is not a superclass of " + dialectClazz); - } - - Class clazz = dialectClazz; - Resource resource = null; - while (resource == null) - { - // Do replacement - String newLocation = location.replaceAll(DEFAULT_DIALECT_REGEX, clazz.getName()); - resource = super.getResource(newLocation); - if (resource != null && resource.exists()) - { - // Found - break; - } - // Not found - resource = null; - // Are we at the base class? - if (clazz.equals(dialectBaseClazz)) - { - // We don't go any further - break; - } - // Move up the hierarchy - clazz = clazz.getSuperclass(); - if (clazz == null) - { - throw new RuntimeException( - "Non-existent HierarchicalResourceLoaderBean hierarchy: " + - dialectBaseClazz.getName() + " is not a superclass of " + dialectClazz); - } - } - return resource; - } -} diff --git a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java index 08f41c6c7d..6497901ff5 100644 --- a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java +++ b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java @@ -29,6 +29,7 @@ import javax.transaction.SystemException; import javax.transaction.UserTransaction; import org.alfresco.error.StackTraceUtil; +import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.transaction.CannotCreateTransactionException; @@ -211,12 +212,9 @@ public class SpringAwareUserTransaction return true; } - /** - * @see #NAME - */ public String getName() { - return NAME; + return Thread.currentThread().getName() + "-" + GUID.generate(); } public boolean isReadOnly() @@ -413,8 +411,7 @@ public class SpringAwareUserTransaction try { internalTxnInfo = createTransactionIfNecessary( - (Method) null, - (Class) null); // super class will just pass nulls back to us + getTransactionManager(), getTransactionAttribute(null, null), getName()); } catch (CannotCreateTransactionException e) { @@ -577,13 +574,6 @@ public class SpringAwareUserTransaction super.completeTransactionAfterThrowing(txInfo, ex); } - @Override - protected String methodIdentification(Method method) - { - // note: override for debugging purposes - this method called by Spring - return NAME; - } - @Override protected void finalize() throws Throwable { diff --git a/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java b/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java index e6b2d50066..fc4c4e2429 100644 --- a/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java +++ b/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java @@ -36,7 +36,8 @@ import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.ParameterCheck; -import org.springframework.orm.hibernate3.SessionFactoryUtils; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -52,17 +53,25 @@ public abstract class TransactionSupportUtil { private static Log logger = LogFactory.getLog(TransactionSupportUtil.class); - /** - * The order of synchronization set to be 100 less than the Hibernate synchronization order - */ - public static final int SESSION_SYNCHRONIZATION_ORDER = - SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER - 100; - + private static final int SESSION_SYNCHRONIZATION_ORDER = 800; /** resource key to store the transaction synchronizer instance */ - protected static final String RESOURCE_KEY_TXN_SYNCH = "txnSynch"; - /** resource binding during after-completion phase */ - protected static final String RESOURCE_KEY_TXN_COMPLETING = "AlfrescoTransactionSupport.txnCompleting"; - + private static final String RESOURCE_KEY_TXN_SYNCH = "AlfrescoTransactionSupport.txnSynch"; + /** resource key to store the transaction id, it needs to live even if the synchronization was cleared */ + private static final String RESOURCE_KEY_TXN_ID = "AlfrescoTransactionSupport.txnId"; + /** + *

+ * As in Spring 5 the synchronisations are cleared after the transaction is committed or rolled back, + * it is required to manage the txn resources in a separate thread local. + * This is required to be able to use resources by afterCommit listeners. + *

+ *

+ * If the transaction is suspended the resources are saved and restored afterwards. + * See {@link TransactionSynchronizationImpl#suspend()} and {@link TransactionSynchronizationImpl#resume()} + *

+ */ + private static final ThreadLocal txnResources = + ThreadLocal.withInitial(() -> new ResourcesHolder(new HashMap<>(3))); + /** * @return Returns the system time when the transaction started, or -1 if there is no current transaction. */ @@ -71,23 +80,14 @@ public abstract class TransactionSupportUtil /* * This method can be called outside of a transaction, so we can go direct to the synchronizations. */ - TransactionSynchronizationImpl txnSynch = - (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); - if (txnSynch == null) + if (TransactionSynchronizationManager.isSynchronizationActive()) { - if (TransactionSynchronizationManager.isSynchronizationActive()) - { - // need to lazily register synchronizations - return registerSynchronizations().getTransactionStartTime(); - } - else - { - return -1; // not in a transaction - } + // need to lazily register synchronizations + return TransactionSupportUtil.getSynchronization().getTransactionStartTime(); } else { - return txnSynch.getTransactionStartTime(); + return -1; // not in a transaction } } @@ -103,25 +103,7 @@ public abstract class TransactionSupportUtil * Go direct to the synchronizations as we don't want to register a resource if one doesn't exist. * This method is heavily used, so the simple Map lookup on the ThreadLocal is the fastest. */ - - TransactionSynchronizationImpl txnSynch = - (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); - if (txnSynch == null) - { - if (TransactionSynchronizationManager.isSynchronizationActive()) - { - // need to lazily register synchronizations - return registerSynchronizations().getTransactionId(); - } - else - { - return null; // not in a transaction - } - } - else - { - return txnSynch.getTransactionId(); - } + return getResource(RESOURCE_KEY_TXN_ID); } public static boolean isActualTransactionActive() @@ -130,117 +112,134 @@ public abstract class TransactionSupportUtil } /** - * Gets a resource associated with the current transaction, which must be active. - *

- * All necessary synchronization instances will be registered automatically, if required. - * + * Gets a resource associated with the current transaction * * @param key the thread resource map key * @return Returns a thread resource of null if not present - * - * @see org.alfresco.repo.transaction.TransactionalResourceHelper for helper methods to create and bind common collection types */ @SuppressWarnings("unchecked") - public static R getResource(Object key) + public static R getResource(Object key) { - // get the synchronization - TransactionSynchronizationImpl txnSynch = getSynchronization(); - // get the resource - Object resource = txnSynch.resources.get(key); - // done + // The resources might be requested outside of the active txn (post completion) + // The resource requested might be the txn synchronization itself or txn id, + // which might be not created and registered yet + if (TransactionSynchronizationManager.isSynchronizationActive()) + { + TransactionSupportUtil.getSynchronization(); + } + Object resource = txnResources.get().resources.get(key); if (logger.isTraceEnabled()) { - logger.trace("Fetched resource: \n" + + logger.trace("Fetched resource in " + TransactionSynchronizationManager.getCurrentTransactionName() + + ": \n" + " key: " + key + "\n" + " resource: " + resource); } return (R) resource; } - + /** - * Gets the current transaction synchronization instance, which contains the locally bound - * resources that are available to {@link #getResource(Object) retrieve} or - * {@link #bindResource(Object, Object) add to}. - *

- * This method also ensures that the transaction binding has been performed. + * Registers new transaction synchronization instance in {@link TransactionSynchronizationManager} and + * creates necessary resources, see {@link #txnResources} * - * @return Returns the common synchronization instance used + * @return Returns new synchronization implementation */ - private static TransactionSynchronizationImpl getSynchronization() + private static TransactionSynchronizationImpl registerSynchronization() { - // ensure synchronizations - return registerSynchronizations(); - } - - /** - * Binds the Alfresco-specific to the transaction resources - * - * @return Returns the current or new synchronization implementation - */ - private static TransactionSynchronizationImpl registerSynchronizations() - { - /* - * No thread synchronization or locking required as the resources are all threadlocal - */ if (!TransactionSynchronizationManager.isSynchronizationActive()) { Thread currentThread = Thread.currentThread(); throw new AlfrescoRuntimeException("Transaction must be active and synchronization is required: " + currentThread); } - TransactionSynchronizationImpl txnSynch = - (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); - if (txnSynch != null) - { - // synchronization already registered - return txnSynch; - } - // we need a unique ID for the transaction + + // a unique ID for the transaction is required String txnId = GUID.generate(); - // register the synchronization - txnSynch = new TransactionSynchronizationImpl(txnId); + TransactionSynchronizationImpl txnSynch = new TransactionSynchronizationImpl(txnId); TransactionSynchronizationManager.registerSynchronization(txnSynch); - // register the resource that will ensure we don't duplication the synchronization - TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); + // save the synchronization to ensure we don't duplicate it + // it might be required to create a nested resource holder + ResourcesHolder resourcesHolder = txnResources.get(); + if (!resourcesHolder.resources.isEmpty()) + { + ResourcesHolder newResourcesHolder = new ResourcesHolder(resourcesHolder, new HashMap<>(3)); + txnResources.set(newResourcesHolder); + } + Map data = txnResources.get().resources; + data.put(RESOURCE_KEY_TXN_SYNCH, txnSynch); + data.put(RESOURCE_KEY_TXN_ID, txnId); // done if (logger.isDebugEnabled()) { - logger.debug("Bound txn synch: " + txnSynch); + logger.debug("Bound txn synch: " + txnSynch + " with txn name: " + + TransactionSynchronizationManager.getCurrentTransactionName()); } return txnSynch; } - + /** - * Cleans out transaction resources if present + * Gets the current transaction synchronization instance if the transaction was not completed + * + * @return Returns the current or new synchronization implementation */ - private static void clearSynchronization() + private static TransactionSynchronizationImpl getSynchronization() { - if (TransactionSynchronizationManager.hasResource(RESOURCE_KEY_TXN_SYNCH)) + Map data = txnResources.get().resources; + if (data.get(RESOURCE_KEY_TXN_SYNCH) != null) { - Object txnSynch = TransactionSynchronizationManager.unbindResource(RESOURCE_KEY_TXN_SYNCH); - // done - if (logger.isDebugEnabled()) - { - logger.debug("Unbound txn synch:" + txnSynch); - } + return (TransactionSynchronizationImpl) data.get(RESOURCE_KEY_TXN_SYNCH); + } + else + { + return TransactionSupportUtil.registerSynchronization(); } } - + /** - * Helper method to rebind the synchronization to the transaction - * - * @param txnSynch TransactionSynchronizationImpl + * Saves resources and creates a new empty resource holder */ - private static void rebindSynchronization(TransactionSynchronizationImpl txnSynch) + private static void suspendSynchronization() { - TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); + ResourcesHolder currentResourcesHolder = txnResources.get(); + ResourcesHolder newResourcesHolder = new ResourcesHolder(currentResourcesHolder, new HashMap<>(3)); + txnResources.set(newResourcesHolder); + } + + /** + * Cleans up the resource holder if it is empty. This is required when transaction was suspended + * but none of application transactions were started before it was resumed. + */ + private static void resumeSynchronization() + { + ResourcesHolder currentResourcesHolder = txnResources.get(); + ResourcesHolder previousResourcesHolder = currentResourcesHolder.previousResourceHolder; + if (currentResourcesHolder.resources.isEmpty() && + previousResourcesHolder != null) + { + txnResources.set(previousResourcesHolder); + } + } + + /** + * Cleans all thread local transaction resources. Restores parent transaction resources if necessary + */ + private static void clearResources() + { + ResourcesHolder currentResourcesHolder = txnResources.get(); + Map txnData = currentResourcesHolder.resources; + txnData.clear(); if (logger.isDebugEnabled()) { - logger.debug("Bound (rebind) txn synch: " + txnSynch); + logger.debug("Clear txn resources for " + Thread.currentThread().getName()); + } + ResourcesHolder previousResourcesHolder = currentResourcesHolder.previousResourceHolder; + if (previousResourcesHolder != null) + { + txnResources.set(previousResourcesHolder); } } - + /** - * Binds a resource to the current transaction, which must be active. + * Binds a resource to the current transaction *

* All necessary synchronization instances will be registered automatically, if required. * @@ -249,14 +248,17 @@ public abstract class TransactionSupportUtil */ public static void bindResource(Object key, Object resource) { - // get the synchronization - TransactionSynchronizationImpl txnSynch = getSynchronization(); - // bind the resource - txnSynch.resources.put(key, resource); - // done - if (logger.isDebugEnabled()) + // The resources should be still available outside of active txn (post completion) + // If the txn is active the synchronization must be created and registered if it doesn't exist yet + if (TransactionSynchronizationManager.isSynchronizationActive()) { - logger.debug("Bound resource: \n" + + TransactionSupportUtil.getSynchronization(); + } + txnResources.get().resources.put(key, resource); + // done + if (logger.isTraceEnabled()) + { + logger.trace("Bound resource to " + TransactionSynchronizationManager.getCurrentTransactionName() + ": \n" + " key: " + key + "\n" + " resource: " + resource); } @@ -264,21 +266,15 @@ public abstract class TransactionSupportUtil /** * Unbinds a resource from the current transaction, which must be active. - *

- * All necessary synchronization instances will be registered automatically, if required. - * * @param key Object */ public static void unbindResource(Object key) { - // get the synchronization - TransactionSynchronizationImpl txnSynch = getSynchronization(); - // remove the resource - txnSynch.resources.remove(key); - // done - if (logger.isDebugEnabled()) + txnResources.get().resources.remove(key); + if (logger.isTraceEnabled()) { - logger.debug("Unbound resource: \n" + + logger.trace("Unbound resource from " + TransactionSynchronizationManager.getCurrentTransactionName() + + ": \n" + " key: " + key); } } @@ -298,7 +294,7 @@ public abstract class TransactionSupportUtil { logger.debug("Bind Listener listener: " + listener + ", priority: " + priority); } - TransactionSynchronizationImpl synch = getSynchronization(); + TransactionSynchronizationImpl synch = TransactionSupportUtil.getSynchronization(); return synch.addListener(listener, priority); } @@ -308,22 +304,49 @@ public abstract class TransactionSupportUtil public static Set getListeners() { // get the synchronization - TransactionSynchronizationImpl txnSynch = getSynchronization(); + TransactionSynchronizationImpl txnSynch = TransactionSupportUtil.getSynchronization(); return txnSynch.getListenersIterable(); } - + /** - * Handler of txn synchronization callbacks specific to internal - * application requirements + * Resource holder to link all necessary resources for the current transaction. + * Also holds the resources for outer transactions. + * This is used by {@link TransactionSynchronizationImpl#suspend()} + * and {@link TransactionSynchronizationImpl#resume()} + * + */ + private static class ResourcesHolder + { + @Nullable + private ResourcesHolder previousResourceHolder; + @NonNull + private Map resources; + + ResourcesHolder(ResourcesHolder previousResourceHolder, Map resources) + { + this.previousResourceHolder = previousResourceHolder; + this.resources = resources; + } + + ResourcesHolder(Map resources) + { + this(null, resources); + } + } + + /** + * Handler of txn synchronization callbacks specific to internal application requirements. + *

+ * This class is not thread safe. It is expected to be used only for purposes of controlling listeners + * for a single thread per instance. */ private static class TransactionSynchronizationImpl extends TransactionSynchronizationAdapter { private long txnStartTime; private final String txnId; - private final Map resources; - + /** * priority to listeners */ @@ -331,7 +354,7 @@ public abstract class TransactionSupportUtil /** * Sets up the resource map - * + * * @param txnId String */ public TransactionSynchronizationImpl(String txnId) @@ -339,7 +362,6 @@ public abstract class TransactionSupportUtil this.txnStartTime = System.currentTimeMillis(); this.txnId = txnId; priorityLookup.put(0, new LinkedHashSet(5)); - resources = new HashMap(17); } public long getTransactionStartTime() @@ -347,11 +369,6 @@ public abstract class TransactionSupportUtil return txnStartTime; } - public String getTransactionId() - { - return txnId; - } - /** * Add a trasaction listener * @@ -361,27 +378,16 @@ public abstract class TransactionSupportUtil { ParameterCheck.mandatory("listener", listener); - if(this.priorityLookup.containsKey(priority)) + if (this.priorityLookup.containsKey(priority)) { Set listeners = priorityLookup.get(priority); return listeners.add(listener); } else { - synchronized (priorityLookup) - { - if(priorityLookup.containsKey(priority)) - { - Set listeners = priorityLookup.get(priority); - return listeners.add(listener); - } - else - { - Set listeners = new LinkedHashSet(5); - priorityLookup.put(priority, listeners); - return listeners.add(listener); - } - } + Set listeners = new LinkedHashSet(5); + priorityLookup.put(priority, listeners); + return listeners.add(listener); } } @@ -393,7 +399,7 @@ public abstract class TransactionSupportUtil private List getLevelZeroListenersIterable() { Setlisteners = priorityLookup.get(0); - return new ArrayList(listeners); + return new ArrayList(listeners); } /** @@ -421,9 +427,6 @@ public abstract class TransactionSupportUtil return sb.toString(); } - /** - * @see org.alfresco.repo.transaction.AlfrescoTransactionSupport#SESSION_SYNCHRONIZATION_ORDER - */ @Override public int getOrder() { @@ -437,7 +440,7 @@ public abstract class TransactionSupportUtil { logger.debug("Suspending transaction: " + this); } - TransactionSupportUtil.clearSynchronization(); + TransactionSupportUtil.suspendSynchronization(); } @Override @@ -447,7 +450,7 @@ public abstract class TransactionSupportUtil { logger.debug("Resuming transaction: " + this); } - TransactionSupportUtil.rebindSynchronization(this); + TransactionSupportUtil.resumeSynchronization(); } /** @@ -465,8 +468,7 @@ public abstract class TransactionSupportUtil logger.debug("Before commit " + (readOnly ? "read-only" : "" ) + this); } // get the txn ID - TransactionSynchronizationImpl synch = (TransactionSynchronizationImpl) - TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); + TransactionSynchronizationImpl synch = getResource(RESOURCE_KEY_TXN_SYNCH); if (synch == null) { throw new AlfrescoRuntimeException("No synchronization bound to thread"); @@ -539,7 +541,7 @@ public abstract class TransactionSupportUtil doBeforeCommit(visitedListeners, readOnly); } } - + @Override public void beforeCompletion() { @@ -553,11 +555,15 @@ public abstract class TransactionSupportUtil listener.beforeCompletion(); } } - @Override public void afterCompletion(int status) { + // As in Spring 5 the synchronisations are cleared after the transaction is committed or rolled back. + // It is required to remove synchronization, as it is enforces binding of + // new txn synchronization if one will be started in afterCommit/afterRollback listeners. + TransactionSupportUtil.unbindResource(RESOURCE_KEY_TXN_SYNCH); + String statusStr = "unknown"; switch (status) { @@ -573,22 +579,18 @@ public abstract class TransactionSupportUtil { logger.debug("After completion (" + statusStr + "): " + this); } - - // Force any queries for read-write state to return TXN_READ_ONLY - // This will be cleared with the synchronization, so we don't need to clear it out - TransactionSupportUtil.bindResource(RESOURCE_KEY_TXN_COMPLETING, Boolean.TRUE); - + Set priorities = priorityLookup.keySet(); - + SortedSet sortedPriorities = new ConcurrentSkipListSet(REVERSE_INTEGER_ORDER); sortedPriorities.addAll(priorities); - + // Need to run these in reverse order cache,lucene,listeners for(Integer priority : sortedPriorities) { Set listeners = new HashSet(priorityLookup.get(priority)); - for(TransactionListener listener : listeners) + for(TransactionListener listener : listeners) { try { @@ -603,7 +605,7 @@ public abstract class TransactionSupportUtil } catch (RuntimeException e) { - logger.error("After completion (" + statusStr + ") TransactionalCache exception", e); + logger.error("After completion (" + statusStr + ") exception", e); } } } @@ -611,10 +613,8 @@ public abstract class TransactionSupportUtil { logger.debug("After Completion: DONE"); } - - - // clear the thread's registrations and synchronizations - TransactionSupportUtil.clearSynchronization(); + + TransactionSupportUtil.clearResources(); } } @@ -622,7 +622,7 @@ public abstract class TransactionSupportUtil { @Override public int compare(Integer arg0, Integer arg1) - { + { return arg0.intValue() - arg1.intValue(); } } ; @@ -631,7 +631,7 @@ public abstract class TransactionSupportUtil { @Override public int compare(Integer arg0, Integer arg1) - { + { return arg1.intValue() - arg0.intValue(); } } ; diff --git a/src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java b/src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java deleted file mode 100644 index 09a0e5be8d..0000000000 --- a/src/test/java/org/alfresco/hibernate/DialectFactoryBeanTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2005-2017 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.hibernate; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import org.hibernate.cfg.Environment; -import org.junit.Test; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - - -/** - * @author Erik Knizat - */ -public class DialectFactoryBeanTest -{ - private static final String MARIADB_DIALECT_NAME = "org.alfresco.repo.domain.hibernate.dialect.AlfrescoMariaDBDialect"; - private static final String MARIA_DB_DRIVER_NAME = "MariaDB connector/J"; - - @Test - public void testMariaDBDialectGetsAdded() - { - DialectFactoryBean dfb = new DialectFactoryBean(); - Map driverDialectMap = new HashMap<>(); - driverDialectMap.put(MARIA_DB_DRIVER_NAME, MARIADB_DIALECT_NAME); - dfb.setDriverDialectMap(driverDialectMap); - Properties props = new Properties(); - dfb.overrideDialectPropertyForDriver(props, MARIA_DB_DRIVER_NAME); - - assertNotNull("The dialect property was not set for the driver.", props.getProperty((Environment.DIALECT))); - assertEquals("Dialect name did not match.", MARIADB_DIALECT_NAME, props.getProperty((Environment.DIALECT))); - } - - @Test - public void testDialectNotAddedIfNotSpecifiedForDriver() - { - DialectFactoryBean dfb = new DialectFactoryBean(); - Map driverDialectMap = new HashMap<>(); - dfb.setDriverDialectMap(driverDialectMap); // Add empty dialect driver map - Properties props = new Properties(); - dfb.overrideDialectPropertyForDriver(props, MARIA_DB_DRIVER_NAME); - - assertNull("Dialect name property was set for unspecified driver name.", props.getProperty((Environment.DIALECT))); - } - -} diff --git a/src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java b/src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java deleted file mode 100644 index 6f1721313c..0000000000 --- a/src/test/java/org/alfresco/ibatis/HierarchicalSqlSessionFactoryBeanTest.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.ibatis; - -import java.util.AbstractCollection; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.TreeSet; - -import junit.framework.TestCase; - -import org.alfresco.util.resource.HierarchicalResourceLoader; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.ibatis.mapping.MappedStatement; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.SqlSessionFactory; -import org.springframework.context.support.ClassPathXmlApplicationContext; - - -/** - * @see HierarchicalSqlSessionFactoryBean - * @see HierarchicalXMLConfigBuilder - * @see HierarchicalResourceLoader - * - * @author Derek Hulley, janv - * @since 4.0 - */ -public class HierarchicalSqlSessionFactoryBeanTest extends TestCase -{ - private static final String QUERY_OBJECT = Object.class.getName(); - private static final String QUERY_ABSTRACTCOLLECTION = "org.alfresco.ibatis.abstractcollection."+AbstractCollection.class.getName().replace(".", "_"); - private static final String QUERY_ABSTRACTLIST = "org.alfresco.ibatis.abstractlist."+AbstractList.class.getName().replace(".", "_"); - private static final String QUERY_TREESET = "org.alfresco.ibatis.treeset."+TreeSet.class.getName().replace(".", "_"); - - private static Log logger = LogFactory.getLog(HierarchicalSqlSessionFactoryBeanTest.class); - - private ClassPathXmlApplicationContext ctx; - private TestDAO testDao; - - @Override - public void setUp() throws Exception - { - testDao = new TestDAO(); - testDao.setId(5L); - testDao.setPropOne("prop-one"); - testDao.setPropTwo("prop-two"); - } - - @Override - public void tearDown() throws Exception - { - try - { - if (ctx != null) - { - ctx.close(); - } - } - catch (Throwable e) - { - logger.error("Failed to neatly close application context", e); - } - } - - /** - * Pushes the dialect class into the system properties, closes an current context and - * recreates it; the MyBatis Configuration is then returned. - */ - @SuppressWarnings("unchecked") - private Configuration getConfiguration(Class dialectClass) throws Exception - { - System.setProperty("hierarchy-test.dialect", dialectClass.getName()); - if (ctx != null) - { - try - { - ctx.close(); - ctx = null; - } - catch (Throwable e) - { - logger.error("Failed to neatly close application context", e); - } - } - ctx = new ClassPathXmlApplicationContext("ibatis/hierarchy-test/hierarchy-test-context.xml"); - return ((SqlSessionFactory)ctx.getBean("mybatisConfig")).getConfiguration(); - } - - /** - * Check context startup and shutdown - */ - public void testContextStartup() throws Exception - { - getConfiguration(TreeSet.class); - getConfiguration(HashSet.class); - getConfiguration(ArrayList.class); - getConfiguration(AbstractCollection.class); - try - { - getConfiguration(Collection.class); - fail("Failed to detect incompatible class hierarchy"); - } - catch (Throwable e) - { - // Expected - } - } - - public void testHierarchyTreeSet() throws Exception - { - Configuration mybatisConfig = getConfiguration(TreeSet.class); - MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_TREESET); - assertNotNull("Query missing for " + QUERY_TREESET + " using " + TreeSet.class, stmt); - try - { - mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); - fail("Query not missing for " + QUERY_ABSTRACTCOLLECTION + " using " + TreeSet.class); - } - catch (IllegalArgumentException e) - { - // Expected - } - } - - public void testHierarchyHashSet() throws Exception - { - Configuration mybatisConfig = getConfiguration(HashSet.class); - MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); - assertNotNull("Query missing for " + QUERY_ABSTRACTCOLLECTION + " using " + HashSet.class, stmt); - try - { - mybatisConfig.getMappedStatement(QUERY_OBJECT); - fail("Query not missing for " + QUERY_OBJECT + " using " + HashSet.class); - } - catch (IllegalArgumentException e) - { - // Expected - } - } - - public void testHierarchyArrayList() throws Exception - { - Configuration mybatisConfig = getConfiguration(ArrayList.class); - MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_ABSTRACTLIST); - assertNotNull("Query missing for " + QUERY_ABSTRACTLIST + " using " + ArrayList.class, stmt); - try - { - mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); - fail("Query not missing for " + QUERY_ABSTRACTCOLLECTION + " using " + ArrayList.class); - } - catch (IllegalArgumentException e) - { - // Expected - } - } - - public void testHierarchyAbstractCollection() throws Exception - { - Configuration mybatisConfig = getConfiguration(AbstractCollection.class); - MappedStatement stmt = mybatisConfig.getMappedStatement(QUERY_ABSTRACTCOLLECTION); - assertNotNull("Query missing for " + QUERY_ABSTRACTCOLLECTION + " using " + AbstractCollection.class, stmt); - try - { - mybatisConfig.getMappedStatement(QUERY_OBJECT); - fail("Query not missing for " + QUERY_OBJECT + " using " + AbstractCollection.class); - } - catch (IllegalArgumentException e) - { - // Expected - } - } - - /** - * Helper class that iBatis will use in the test mappings - * @author Derek Hulley - */ - public static class TestDAO - { - private Long id; - private String propOne; - private String propTwo; - - public Long getId() - { - return id; - } - public void setId(Long id) - { - this.id = id; - } - public String getPropOne() - { - return propOne; - } - public void setPropOne(String propOne) - { - this.propOne = propOne; - } - public String getPropTwo() - { - return propTwo; - } - public void setPropTwo(String propTwo) - { - this.propTwo = propTwo; - } - } -} diff --git a/src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java b/src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java deleted file mode 100644 index 1cd11a6022..0000000000 --- a/src/test/java/org/alfresco/util/bean/HierarchicalBeanLoaderTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.util.bean; - -import java.util.AbstractCollection; -import java.util.AbstractList; -import java.util.Collection; -import java.util.TreeSet; - -import junit.framework.TestCase; - -import org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * @see HierarchicalBeanLoader - * - * @author Derek Hulley - * @since 3.2SP1 - */ -public class HierarchicalBeanLoaderTest extends TestCase -{ - private ClassPathXmlApplicationContext ctx; - - private String getBean(Class clazz, boolean setBeforeInit) throws Exception - { - if (setBeforeInit) - { - System.setProperty("hierarchy-test.dialect", clazz.getName()); - } - ctx = new ClassPathXmlApplicationContext("bean-loader/hierarchical-bean-loader-test-context.xml"); - if (!setBeforeInit) - { - System.setProperty("hierarchy-test.dialect", clazz.getName()); - } - return (String) ctx.getBean("test.someString"); - } - - public void tearDown() - { - try - { - ctx.close(); - } - catch (Throwable e) - { - } - } - - public void testSuccess1() throws Throwable - { - String str = getBean(TreeSet.class, true); - assertEquals("Bean value incorrect", "TreeSet", str); - } - - public void testSuccess2() throws Throwable - { - String str = getBean(AbstractList.class, true); - assertEquals("Bean value incorrect", "AbstractList", str); - } - - public void testSuccess3() throws Throwable - { - String str = getBean(AbstractCollection.class, true); - assertEquals("Bean value incorrect", "AbstractCollection", str); - } - - public void testFailure1() throws Throwable - { - try - { - getBean(Collection.class, true); - fail("Should not be able to retrieve bean using class " + Collection.class); - } - catch (Throwable e) - { - e.printStackTrace(); - } - } -} diff --git a/src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java b/src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java deleted file mode 100644 index 1fa11f6b97..0000000000 --- a/src/test/java/org/alfresco/util/resource/HierarchicalResourceLoaderTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.util.resource; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.AbstractCollection; -import java.util.AbstractList; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.TreeSet; - -import junit.framework.TestCase; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.springframework.core.io.Resource; - -/** - * @see HierarchicalResourceLoader - * - * @author Derek Hulley - * @since 3.2 (Mobile) - */ -public class HierarchicalResourceLoaderTest extends TestCase -{ - private HierarchicalResourceLoader getLoader( - Class baseClazz, - Class clazz) throws Throwable - { - HierarchicalResourceLoader loader = new HierarchicalResourceLoader(); - loader.setDialectBaseClass(baseClazz.getName()); - loader.setDialectClass(clazz.getName()); - loader.afterPropertiesSet(); - return loader; - } - - /** - * Check that unmatched hierarchies are detected - */ - public void testMismatchDetection() throws Throwable - { - // First, do a successful few - getLoader(AbstractCollection.class, TreeSet.class); - getLoader(AbstractCollection.class, HashSet.class); - getLoader(AbstractCollection.class, ArrayList.class); - getLoader(AbstractCollection.class, AbstractCollection.class); - // Now blow up a bit - try - { - getLoader(Collection.class, Object.class).getResource("abc"); - fail("Failed to detect incompatible class hierarchy"); - } - catch (RuntimeException e) - { - // Expected - } - try - { - getLoader(ArrayList.class, AbstractCollection.class).getResource("abc"); - fail("Failed to detect incompatible class hierarchy"); - } - catch (RuntimeException e) - { - // Expected - } - } - - private void checkResource(Resource resource, String check) throws Throwable - { - assertNotNull("Resource not found", resource); - assertTrue("Resource doesn't exist", resource.exists()); - InputStream is = resource.getInputStream(); - StringBuilder builder = new StringBuilder(128); - byte[] bytes = new byte[128]; - InputStream tempIs = null; - try - { - tempIs = new BufferedInputStream(is, 128); - int count = -2; - while (count != -1) - { - // do we have something previously read? - if (count > 0) - { - String toWrite = new String(bytes, 0, count, "UTF-8"); - builder.append(toWrite); - } - // read the next set of bytes - count = tempIs.read(bytes); - } - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("Unable to read stream", e); - } - finally - { - // close the input stream - try - { - is.close(); - } - catch (Exception e) - { - } - } - // The string - String fileValue = builder.toString(); - assertEquals("Incorrect file retrieved: ", check, fileValue); - } - - private static final String RESOURCE = "classpath:resource-loader/#resource.dialect#/file.txt"; - /** - * Check that resource loading works. - * - * The data available is: - *

-     * classpatch:resource-loader/
-     *    java.util.AbstractCollection
-     *    java.util.AbstractList
-     *    java.util.TreeSet 
-     * 
- * With each folder containing a text file with the name of the folder. - */ - public void testResourceLoading() throws Throwable - { - // First, do a successful few - HierarchicalResourceLoader bean; - Resource resource; - - bean = getLoader(AbstractCollection.class, TreeSet.class); - resource = bean.getResource(RESOURCE); - checkResource(resource, "java.util.TreeSet"); - - bean = getLoader(AbstractCollection.class, AbstractSet.class); - resource = bean.getResource(RESOURCE); - checkResource(resource, "java.util.AbstractCollection"); - - bean = getLoader(AbstractCollection.class, AbstractCollection.class); - resource = bean.getResource(RESOURCE); - checkResource(resource, "java.util.AbstractCollection"); - - bean = getLoader(AbstractCollection.class, ArrayList.class); - resource = bean.getResource(RESOURCE); - checkResource(resource, "java.util.AbstractList"); - - bean = getLoader(AbstractCollection.class, AbstractList.class); - resource = bean.getResource(RESOURCE); - checkResource(resource, "java.util.AbstractList"); - } -} diff --git a/src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml b/src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml deleted file mode 100644 index 3cf31df419..0000000000 --- a/src/test/resources/bean-loader/hierarchical-bean-loader-test-context.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - false - - - SYSTEM_PROPERTIES_MODE_OVERRIDE - - - false - - - - - - test.someString.#bean.dialect# - - - java.lang.String - - - java.util.AbstractCollection - - - ${hierarchy-test.dialect} - - - - - - - - - - - - - - diff --git a/src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml b/src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml deleted file mode 100644 index cd3ffda6a4..0000000000 --- a/src/test/resources/ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml b/src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml deleted file mode 100644 index 34ab7f88f2..0000000000 --- a/src/test/resources/ibatis/hierarchy-test/hierarchy-test-context.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - false - - - SYSTEM_PROPERTIES_MODE_OVERRIDE - - - false - - - - - - java.util.AbstractCollection - - - ${hierarchy-test.dialect} - - - - - - - - - - classpath:ibatis/hierarchy-test/hierarchy-test-SqlMapConfig.xml - - - - - - - - - - java:comp/env/jdbc/dataSource - - - - - - - diff --git a/src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml b/src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml deleted file mode 100644 index e1895e60bc..0000000000 --- a/src/test/resources/ibatis/hierarchy-test/java.util.AbstractCollection/hierarchy-test-SqlMap.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml b/src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml deleted file mode 100644 index 72698980ea..0000000000 --- a/src/test/resources/ibatis/hierarchy-test/java.util.AbstractList/hierarchy-test-SqlMap.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml b/src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml deleted file mode 100644 index b15273e2d8..0000000000 --- a/src/test/resources/ibatis/hierarchy-test/java.util.TreeSet/hierarchy-test-SqlMap.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file From 6d94c84c8e2fe8fc6c119dad2a726bf56a6b895a Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Sun, 4 Mar 2018 13:45:11 +0000 Subject: [PATCH 076/282] [maven-release-plugin] prepare release 7.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c53b7a8fde..f53767dc80 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 6.19-SNAPSHOT + 7.0 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.0 From 6306632572caca976a81952d8e2d2579b3463276 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Sun, 4 Mar 2018 13:45:16 +0000 Subject: [PATCH 077/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f53767dc80..f87aa2e3d4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.0 + 7.1-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.0 + HEAD From fd88a71fcd1ae10ab0ef2e9c07e0d73c9c6bd982 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Sun, 4 Mar 2018 13:54:00 +0000 Subject: [PATCH 078/282] Update readme info about version 7 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8abfef4581..79d6eecb86 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ The library contains the following: * Canned queries interface and supporting classes * Generic encryption supporting classes +Version 7 of the library uses Spring 5, Quartz 2.3 and does not have Hibernate dependency. ### Building and testing The project can be built and tested by running Maven command: From a5325a3e5cd290f8c1b560b02979145825f995f1 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Thu, 8 Mar 2018 12:06:02 +0000 Subject: [PATCH 079/282] Add compiler plugin config --- pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pom.xml b/pom.xml index f87aa2e3d4..12c0a1d43f 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,8 @@ 5.0.4.RELEASE 6.8 + + 1.8 @@ -229,6 +231,15 @@ + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.0 + + ${java.compiler.version} + ${java.compiler.version} + + From 62bf93c0deb08e4c96140892333d84e2702f500f Mon Sep 17 00:00:00 2001 From: Cristian Turlica Date: Fri, 16 Mar 2018 14:09:25 +0200 Subject: [PATCH 080/282] REPO-1287: Upgrade jaxb-xjc, jaxb-impl - upgraded to version 2.3.0 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 12c0a1d43f..7fe15a8af8 100644 --- a/pom.xml +++ b/pom.xml @@ -127,17 +127,17 @@ com.sun.xml.bind jaxb-xjc - 2.2.11 + 2.3.0 com.sun.xml.bind jaxb-impl - 2.2.11 + 2.3.0 com.sun.xml.bind jaxb-core - 2.2.11 + 2.3.0 dom4j From 16a8434db68d59639a7414df883247537359c4ca Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 16 Mar 2018 14:08:12 +0000 Subject: [PATCH 081/282] [maven-release-plugin] prepare release 7.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7fe15a8af8..e9d2c3dfb2 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.1-SNAPSHOT + 7.1 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.1 From 239f6582ac511c765af36b79b07b2590478a7166 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 16 Mar 2018 14:08:17 +0000 Subject: [PATCH 082/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e9d2c3dfb2..8e6722bd11 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.1 + 7.2-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.1 + HEAD From 68fe3ae0a952aef595522da2172ae2b2c3c710d3 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Mon, 9 Apr 2018 16:13:17 +0100 Subject: [PATCH 083/282] REPO-3447 Remove c3p0 (#12) Quartz uses c3p0 jdbc pool for it's distributed job storage since version 2.x This commit will exclude c3p0 lib as distributed job storage is not used. --- pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index 8e6722bd11..e41bd1a365 100644 --- a/pom.xml +++ b/pom.xml @@ -118,6 +118,14 @@ org.quartz-scheduler quartz 2.3.0 + + + + + com.mchange + * + + org.alfresco.surf From f1ff360f48ca60740b931f31d0cd9d54b4777cd2 Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Tue, 10 Apr 2018 13:57:53 +0100 Subject: [PATCH 084/282] REOP-3422 Improve the log messages so we can seen when transformers in docker containers restart --- src/main/resources/log4j.properties | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index c9c313274d..ac1567b8b4 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -265,3 +265,8 @@ log4j.logger.org.alfresco.enterprise.repo.authorization.AuthorizationsConsistenc # HeartBeat log4j.logger.org.alfresco.heartbeat=info + +# Transformations +log4j.logger.org.alfresco.repo.content.transform.JodContentTransformer=info +log4j.logger.org.alfresco.repo.content.transform.magick.ImageMagickContentTransformerWorker=info +log4j.logger.org.alfresco.repo.content.transform.pdfrenderer.AlfrescoPdfRendererContentTransformerWorker=info \ No newline at end of file From 3318c6cd949ea8650d2536d5b404b632c94cec04 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 11 Apr 2018 12:12:41 +0000 Subject: [PATCH 085/282] [maven-release-plugin] prepare release 7.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e41bd1a365..8a4481b088 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.2-SNAPSHOT + 7.2 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.2 From d977fb7e229f39fb993f86666ba2509d8b77b9c1 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 11 Apr 2018 12:12:48 +0000 Subject: [PATCH 086/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8a4481b088..aa487fcc63 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.2 + 7.3-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.2 + HEAD From d1684ebf5cc219fede0a6a9a22517a820a704168 Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Wed, 30 May 2018 20:36:43 +0300 Subject: [PATCH 087/282] REPO-2974: Products that reuse the alfresco-core artifact get an unwanted alfresco.log (#13) * Remove log configs from alfresco-core (they will be moved to the packaging projects). --- src/main/resources/log4j.properties | 272 -------------------------- src/main/resources/logging.properties | 8 - 2 files changed, 280 deletions(-) delete mode 100644 src/main/resources/log4j.properties delete mode 100644 src/main/resources/logging.properties diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties deleted file mode 100644 index ac1567b8b4..0000000000 --- a/src/main/resources/log4j.properties +++ /dev/null @@ -1,272 +0,0 @@ -# Set root logger level to error -log4j.rootLogger=error, Console, File - -###### Console appender definition ####### - -# All outputs currently set to be a ConsoleAppender. -log4j.appender.Console=org.apache.log4j.ConsoleAppender -log4j.appender.Console.layout=org.apache.log4j.PatternLayout - -# use log4j NDC to replace %x with tenant domain / username -log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} %x %-5p [%c{3}] [%t] %m%n -#log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} %-5p [%c] %m%n - -###### File appender definition ####### -log4j.appender.File=org.apache.log4j.DailyRollingFileAppender -log4j.appender.File.File=alfresco.log -log4j.appender.File.Append=true -log4j.appender.File.DatePattern='.'yyyy-MM-dd -log4j.appender.File.layout=org.apache.log4j.PatternLayout -log4j.appender.File.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} %-5p [%c] [%t] %m%n - -###### Hibernate specific appender definition ####### -#log4j.appender.file=org.apache.log4j.FileAppender -#log4j.appender.file.File=hibernate.log -#log4j.appender.file.layout=org.apache.log4j.PatternLayout -#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -###### Log level overrides ####### - -# Commented-in loggers will be exposed as JMX MBeans (refer to org.alfresco.repo.admin.Log4JHierarchyInit) -# Hence, generally useful loggers should be listed with at least ERROR level to allow simple runtime -# control of the level via a suitable JMX Console. Also, any other loggers can be added transiently via -# Log4j addLoggerMBean as long as the logger exists and has been loaded. - -# Hibernate -log4j.logger.org.hibernate=error -log4j.logger.org.hibernate.util.JDBCExceptionReporter=fatal -log4j.logger.org.hibernate.event.def.AbstractFlushingEventListener=fatal -log4j.logger.org.hibernate.type=warn -log4j.logger.org.hibernate.cfg.SettingsFactory=warn - -# Spring -log4j.logger.org.springframework=warn -# Turn off Spring remoting warnings that should really be info or debug. -log4j.logger.org.springframework.remoting.support=error -log4j.logger.org.springframework.util=error - -# Axis/WSS4J -log4j.logger.org.apache.axis=info -log4j.logger.org.apache.ws=info - -# CXF -log4j.logger.org.apache.cxf=error - -# MyFaces -log4j.logger.org.apache.myfaces.util.DebugUtils=info -log4j.logger.org.apache.myfaces.el.VariableResolverImpl=error -log4j.logger.org.apache.myfaces.application.jsp.JspViewHandlerImpl=error -log4j.logger.org.apache.myfaces.taglib=error - -# log prepared statement cache activity ### -log4j.logger.org.hibernate.ps.PreparedStatementCache=info - -# Alfresco -log4j.logger.org.alfresco=error -log4j.logger.org.alfresco.repo.admin=info -log4j.logger.org.alfresco.repo.transaction=warn -log4j.logger.org.alfresco.repo.cache.TransactionalCache=warn -log4j.logger.org.alfresco.repo.model.filefolder=warn -log4j.logger.org.alfresco.repo.tenant=info -log4j.logger.org.alfresco.config=warn -log4j.logger.org.alfresco.config.JndiObjectFactoryBean=warn -log4j.logger.org.alfresco.config.JBossEnabledWebApplicationContext=warn -log4j.logger.org.alfresco.repo.management.subsystems=warn -log4j.logger.org.alfresco.repo.management.subsystems.ChildApplicationContextFactory=info -log4j.logger.org.alfresco.repo.management.subsystems.ChildApplicationContextFactory$ChildApplicationContext=warn -log4j.logger.org.alfresco.repo.security.sync=info -log4j.logger.org.alfresco.repo.security.person=info - -log4j.logger.org.alfresco.sample=info -log4j.logger.org.alfresco.web=info -#log4j.logger.org.alfresco.web.app.AlfrescoNavigationHandler=debug -#log4j.logger.org.alfresco.web.ui.repo.component.UIActions=debug -#log4j.logger.org.alfresco.web.ui.repo.tag.PageTag=debug -#log4j.logger.org.alfresco.web.bean.clipboard=debug -log4j.logger.org.alfresco.service.descriptor.DescriptorService=info -#log4j.logger.org.alfresco.web.page=debug - -log4j.logger.org.alfresco.repo.importer.ImporterBootstrap=error -#log4j.logger.org.alfresco.repo.importer.ImporterBootstrap=info - -log4j.logger.org.alfresco.repo.admin.patch.PatchExecuter=info -log4j.logger.org.alfresco.repo.domain.patch.ibatis.PatchDAOImpl=info - -# Specific patches -log4j.logger.org.alfresco.repo.admin.patch.impl.DeploymentMigrationPatch=info -log4j.logger.org.alfresco.repo.version.VersionMigrator=info - -log4j.logger.org.alfresco.repo.module.ModuleServiceImpl=info -log4j.logger.org.alfresco.repo.domain.schema.SchemaBootstrap=info -log4j.logger.org.alfresco.repo.admin.ConfigurationChecker=info -log4j.logger.org.alfresco.repo.node.index.AbstractReindexComponent=warn -log4j.logger.org.alfresco.repo.node.index.IndexTransactionTracker=warn -log4j.logger.org.alfresco.repo.node.index.FullIndexRecoveryComponent=info -log4j.logger.org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl=warn -log4j.logger.org.alfresco.repo.domain.hibernate.DirtySessionMethodInterceptor=warn -log4j.logger.org.alfresco.repo.transaction.RetryingTransactionHelper=warn -log4j.logger.org.alfresco.util.transaction.SpringAwareUserTransaction.trace=warn -log4j.logger.org.alfresco.util.AbstractTriggerBean=warn -log4j.logger.org.alfresco.enterprise.repo.cluster=info -log4j.logger.org.alfresco.repo.version.Version2ServiceImpl=warn - -#log4j.logger.org.alfresco.web.app.DebugPhaseListener=debug -log4j.logger.org.alfresco.repo.node.db.NodeStringLengthWorker=info - -log4j.logger.org.alfresco.repo.workflow=info - -# CIFS server debugging -log4j.logger.org.alfresco.smb.protocol=error -#log4j.logger.org.alfresco.smb.protocol.auth=debug -#log4j.logger.org.alfresco.acegi=debug - -# FTP server debugging -log4j.logger.org.alfresco.ftp.protocol=error -#log4j.logger.org.alfresco.ftp.server=debug - -# WebDAV debugging -#log4j.logger.org.alfresco.webdav.protocol=debug -log4j.logger.org.alfresco.webdav.protocol=info - -# NTLM servlet filters -#log4j.logger.org.alfresco.web.app.servlet.NTLMAuthenticationFilter=debug -#log4j.logger.org.alfresco.repo.webdav.auth.NTLMAuthenticationFilter=debug - -# Kerberos servlet filters -#log4j.logger.org.alfresco.web.app.servlet.KerberosAuthenticationFilter=debug -#log4j.logger.org.alfresco.repo.webdav.auth.KerberosAuthenticationFilter=debug - -# File servers -log4j.logger.org.alfresco.fileserver=warn - -# Repo filesystem debug logging -#log4j.logger.org.alfresco.filesys.repo.ContentDiskDriver=debug - -# Integrity message threshold - if 'failOnViolation' is off, then WARNINGS are generated -log4j.logger.org.alfresco.repo.node.integrity=ERROR - -# Authentication -# Specifically brute force attack detection -log4j.logger.org.alfresco.repo.security.authentication=warn - -# Indexer debugging -log4j.logger.org.alfresco.repo.search.Indexer=error -#log4j.logger.org.alfresco.repo.search.Indexer=debug - -log4j.logger.org.alfresco.repo.search.impl.lucene.index=error -log4j.logger.org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexerImpl=warn -#log4j.logger.org.alfresco.repo.search.impl.lucene.index=DEBUG - -# Audit debugging -# log4j.logger.org.alfresco.repo.audit=DEBUG -# log4j.logger.org.alfresco.repo.audit.model=DEBUG - -# Property sheet and modelling debugging -# change to error to hide the warnings about missing properties and associations -log4j.logger.alfresco.missingProperties=warn - -# Dictionary/Model debugging -log4j.logger.org.alfresco.repo.dictionary=warn -log4j.logger.org.alfresco.repo.dictionary.types.period=warn - -# Virtualization Server Registry -log4j.logger.org.alfresco.mbeans.VirtServerRegistry=error - -# Spring context runtime property setter -log4j.logger.org.alfresco.util.RuntimeSystemPropertiesSetter=info - -# Debugging options for clustering -log4j.logger.org.alfresco.repo.content.ReplicatingContentStore=error -log4j.logger.org.alfresco.repo.content.replication=error - -#log4j.logger.org.alfresco.repo.deploy.DeploymentServiceImpl=debug - -# Activity service -log4j.logger.org.alfresco.repo.activities=warn - -# User usage tracking -log4j.logger.org.alfresco.repo.usage=info - -# Sharepoint -log4j.logger.org.alfresco.module.vti=info - -# Forms Engine -log4j.logger.org.alfresco.web.config.forms=info -log4j.logger.org.alfresco.web.scripts.forms=info - -# CMIS -log4j.logger.org.alfresco.opencmis=error -log4j.logger.org.alfresco.opencmis.AlfrescoCmisServiceInterceptor=error -log4j.logger.org.alfresco.cmis=error -log4j.logger.org.alfresco.cmis.dictionary=warn -log4j.logger.org.apache.chemistry.opencmis=info -log4j.logger.org.apache.chemistry.opencmis.server.impl.browser.CmisBrowserBindingServlet=OFF -log4j.logger.org.apache.chemistry.opencmis.server.impl.atompub.CmisAtomPubServlet=OFF - -# IMAP -log4j.logger.org.alfresco.repo.imap=info - -# JBPM -# Note: non-fatal errors (eg. logged during job execution) should be handled by Alfresco's retrying transaction handler -log4j.logger.org.jbpm.graph.def.GraphElement=fatal - -#log4j.logger.org.alfresco.repo.googledocs=debug - -###### Scripting ####### - -# Web Framework -log4j.logger.org.springframework.extensions.webscripts=info -log4j.logger.org.springframework.extensions.webscripts.ScriptLogger=warn -log4j.logger.org.springframework.extensions.webscripts.ScriptDebugger=off - -# Repository -log4j.logger.org.alfresco.repo.web.scripts=warn -log4j.logger.org.alfresco.repo.web.scripts.BaseWebScriptTest=info -log4j.logger.org.alfresco.repo.web.scripts.AlfrescoRhinoScriptDebugger=off -log4j.logger.org.alfresco.repo.jscript=error -log4j.logger.org.alfresco.repo.jscript.ScriptLogger=warn -log4j.logger.org.alfresco.repo.cmis.rest.CMISTest=info - -log4j.logger.org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImpl=off -log4j.logger.org.alfresco.repo.domain.schema.script.ScriptExecutorImpl=info - -log4j.logger.org.alfresco.repo.search.impl.solr.facet.SolrFacetServiceImpl=info - -# Bulk Filesystem Import Tool -log4j.logger.org.alfresco.repo.bulkimport=warn - -# Freemarker -# Note the freemarker.runtime logger is used to log non-fatal errors that are handled by Alfresco's retrying transaction handler -log4j.logger.freemarker.runtime= - -# Metadata extraction -log4j.logger.org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter=warn - -# Reduces PDFont error level due to ALF-7105 -log4j.logger.org.apache.pdfbox.pdmodel.font.PDSimpleFont=fatal -log4j.logger.org.apache.pdfbox.pdmodel.font.PDFont=fatal -log4j.logger.org.apache.pdfbox.pdmodel.font.PDCIDFont=fatal - -# no index support -log4j.logger.org.alfresco.repo.search.impl.noindex.NoIndexIndexer=fatal -log4j.logger.org.alfresco.repo.search.impl.noindex.NoIndexSearchService=fatal - -# lucene index warnings -log4j.logger.org.alfresco.repo.search.impl.lucene.index.IndexInfo=warn - -# Warn about RMI socket bind retries. -log4j.logger.org.alfresco.util.remote.server.socket.HostConfigurableSocketFactory=warn - -log4j.logger.org.alfresco.repo.usage.RepoUsageMonitor=info - -# Authorization -log4j.logger.org.alfresco.enterprise.repo.authorization.AuthorizationService=info -log4j.logger.org.alfresco.enterprise.repo.authorization.AuthorizationsConsistencyMonitor=warn - -# HeartBeat -log4j.logger.org.alfresco.heartbeat=info - -# Transformations -log4j.logger.org.alfresco.repo.content.transform.JodContentTransformer=info -log4j.logger.org.alfresco.repo.content.transform.magick.ImageMagickContentTransformerWorker=info -log4j.logger.org.alfresco.repo.content.transform.pdfrenderer.AlfrescoPdfRendererContentTransformerWorker=info \ No newline at end of file diff --git a/src/main/resources/logging.properties b/src/main/resources/logging.properties deleted file mode 100644 index 9e65a1a5ac..0000000000 --- a/src/main/resources/logging.properties +++ /dev/null @@ -1,8 +0,0 @@ -handlers = java.util.logging.ConsoleHandler - -java.util.logging.ConsoleHandler.level = FINE -java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter - -org.activiti.level = FATAL -#Uncomment this to log schema creation statements from Activiti -#org.activiti.engine.impl.db.DbSqlSession.level = FINE From c156a846f9567555fa20b80662a8024a44b13ff6 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 30 May 2018 17:46:46 +0000 Subject: [PATCH 088/282] [maven-release-plugin] prepare release 7.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aa487fcc63..d6c005a291 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.3-SNAPSHOT + 7.3 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.3 From 364458aafe2061d679ce72bb3a45c7255ea518be Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 30 May 2018 17:46:53 +0000 Subject: [PATCH 089/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d6c005a291..48833ad8aa 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.3 + 7.4-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.3 + HEAD From 539dc45e6dd0f3765614ec131d889b56d5093f45 Mon Sep 17 00:00:00 2001 From: Nicolas Barithel Date: Fri, 15 Jun 2018 15:48:44 +0200 Subject: [PATCH 090/282] ALF-22007 fix TransactionListeners execution order Change-Id: I5b5359948a631f17f2030145a847b1db728617a4 --- .../transaction/TransactionSupportUtil.java | 5 +- .../SpringAwareUserTransactionTest.java | 81 +++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java b/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java index fc4c4e2429..05f7bac5a1 100644 --- a/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java +++ b/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java @@ -480,7 +480,7 @@ public abstract class TransactionSupportUtil // These are still considered part of the transaction so are executed here doBeforeCommit(readOnly); - // Now run the > 0 listeners beforeCommit + // Now run the != 0 listeners beforeCommit Set priorities = priorityLookup.keySet(); SortedSet sortedPriorities = new ConcurrentSkipListSet(FORWARD_INTEGER_ORDER); @@ -526,8 +526,7 @@ public abstract class TransactionSupportUtil */ private void doBeforeCommit(Set visitedListeners, boolean readOnly) { - Set listeners = priorityLookup.get(0); - Set pendingListeners = new HashSet(listeners); + List pendingListeners = getLevelZeroListenersIterable(); pendingListeners.removeAll(visitedListeners); if (pendingListeners.size() != 0) diff --git a/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java b/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java index 5c218bbafe..2b11281c5d 100644 --- a/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java +++ b/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java @@ -19,6 +19,7 @@ package org.alfresco.util.transaction; import java.util.NoSuchElementException; +import java.util.Objects; import javax.transaction.RollbackException; import javax.transaction.Status; @@ -321,6 +322,86 @@ public class SpringAwareUserTransactionTest extends TestCase TransactionDefinition.TIMEOUT_DEFAULT); } + public void testTransactionListenerOrder() throws Throwable + { + testNoTxnStatus(); + try + { + txn.begin(); + StringBuffer buffer = new StringBuffer(); + TransactionSupportUtil.bindListener(new TestTransactionListener("5x", buffer), 5); + TransactionSupportUtil.bindListener(new TestTransactionListener("0a", buffer), 0); + TransactionSupportUtil.bindListener(new TestTransactionListener("0e", buffer), 0); + TransactionSupportUtil.bindListener(new TestTransactionListener("0d", buffer), 0); + TransactionSupportUtil.bindListener(new TestTransactionListener("0b", buffer), 0); + TransactionSupportUtil.bindListener(new TestTransactionListener("0c", buffer), 0); + TransactionSupportUtil.bindListener(new TestTransactionListener("4x", buffer), 4); + TransactionSupportUtil.bindListener(new TestTransactionListener("1x", buffer), -1); + TransactionSupportUtil.bindListener(new TestTransactionListener("3a", buffer), 3); + TransactionSupportUtil.bindListener(new TestTransactionListener("3e", buffer), 3); + TransactionSupportUtil.bindListener(new TestTransactionListener("3d", buffer), 3); + TransactionSupportUtil.bindListener(new TestTransactionListener("3b", buffer), 3); + TransactionSupportUtil.bindListener(new TestTransactionListener("3c", buffer), 3); + TransactionSupportUtil.bindListener(new TestTransactionListener("2x", buffer), -2); + txn.commit(); + assertEquals("0a0e0d0b0c2x1x3a3e3d3b3c4x5x", buffer.toString()); + } + catch (Exception e) + { + try + { + txn.rollback(); + } + catch (Exception ee) + { + e.addSuppressed(ee); + } + throw e; + } + checkNoStatusOnThread(); + } + + private static class TestTransactionListener extends TransactionListenerAdapter + { + private final String name; + private final StringBuffer buffer; + + public TestTransactionListener(String name, StringBuffer buffer) + { + Objects.requireNonNull(name); + Objects.requireNonNull(buffer); + this.name = name; + this.buffer = buffer; + } + + @Override + public void beforeCommit(boolean readOnly) + { + buffer.append(name); + } + + public String getName() + { + return name; + } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof TestTransactionListener) + { + return name.equals(((TestTransactionListener) obj).getName()); + } + return false; + } + + @Override + public int hashCode() + { + return name.hashCode(); + } + } + /** * Used to check that the transaction manager is being called correctly * From be7abcbebf532dbb8146426f50648e837a7cae75 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 22 Aug 2018 11:08:27 +0000 Subject: [PATCH 091/282] [maven-release-plugin] prepare release 7.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 48833ad8aa..b4b370671f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.4-SNAPSHOT + 7.4 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.4 From b6aa4932bd856b4ae5c4bde3050c07df2a861b04 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 22 Aug 2018 11:08:33 +0000 Subject: [PATCH 092/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b4b370671f..e6f412ba9f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 9 alfresco-core - 7.4 + 7.5-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.4 + HEAD From f908a3fdd56f72649fd978dca36a3196d43d06f5 Mon Sep 17 00:00:00 2001 From: Martin Muller Date: Tue, 30 Oct 2018 15:40:00 +0000 Subject: [PATCH 093/282] [WIP] REPO-3918 java 11 (#23) * REPO-3918 remove compiler plugin because it is already in super pom * REPO-3918 use latest jaxb libraries * REPO-3918 add xml binding --- pom.xml | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index e6f412ba9f..eeb051648a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-super-pom - 9 + 10 alfresco-core 7.5-SNAPSHOT @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + HEAD @@ -32,8 +32,6 @@ 5.0.4.RELEASE 6.8 - - 1.8 @@ -132,20 +130,25 @@ spring-surf-core-configservice ${dependency.surf.version} + + javax.xml.bind + jaxb-api + 2.3.1 + com.sun.xml.bind jaxb-xjc - 2.3.0 + 2.3.1 com.sun.xml.bind jaxb-impl - 2.3.0 + 2.3.1 com.sun.xml.bind jaxb-core - 2.3.0 + 2.3.0.1 dom4j @@ -230,7 +233,7 @@ maven-jar-plugin - 2.6 + 3.1.0 @@ -239,15 +242,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - 3.6.0 - - ${java.compiler.version} - ${java.compiler.version} - - From b24c74f4264527f7079b03e8abecab789718d534 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Tue, 30 Oct 2018 15:43:53 +0000 Subject: [PATCH 094/282] [maven-release-plugin] prepare release 7.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index eeb051648a..7962364526 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.5-SNAPSHOT + 7.5 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.5 From 03b1d81beeb69ec9c0bce5359ed639e8f8dd51ad Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Tue, 30 Oct 2018 15:43:59 +0000 Subject: [PATCH 095/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7962364526..82c228d9d8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.5 + 7.6-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.5 + HEAD From fdc9f7553911561476d9cf527100c0a1612d1e83 Mon Sep 17 00:00:00 2001 From: Alessandro Benedetti Date: Thu, 22 Nov 2018 16:14:01 +0000 Subject: [PATCH 096/282] =?UTF-8?q?[SEARCH-1263]Date=20caching=20locale=20?= =?UTF-8?q?and=20timezone=20format=20fix=20to=20comply=20wi=E2=80=A6=20(#2?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [SEARCH-1263]Date caching locale and timezone format fix to comply with ISO 8601 +tests * [SEARCH-1263]Tests fixes to check all locales + license --- .../org/alfresco/util/CachingDateFormat.java | 19 ++--- .../alfresco/util/CachingDateFormatTest.java | 72 +++++++++++++++++++ 2 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 src/test/java/org/alfresco/util/CachingDateFormatTest.java diff --git a/src/main/java/org/alfresco/util/CachingDateFormat.java b/src/main/java/org/alfresco/util/CachingDateFormat.java index 263c20c045..6232c4913f 100644 --- a/src/main/java/org/alfresco/util/CachingDateFormat.java +++ b/src/main/java/org/alfresco/util/CachingDateFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2018 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,7 @@ */ package org.alfresco.util; +import java.text.NumberFormat; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; @@ -26,14 +27,11 @@ import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.Map; +import java.util.TimeZone; import java.util.WeakHashMap; -import org.alfresco.error.AlfrescoRuntimeException; -import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.DateTimeFormatterBuilder; import org.joda.time.format.ISODateTimeFormat; -import org.springframework.extensions.surf.exception.PlatformRuntimeException; /** * Provides thread safe means of obtaining a cached date formatter. @@ -46,6 +44,7 @@ import org.springframework.extensions.surf.exception.PlatformRuntimeException; */ public class CachingDateFormat extends SimpleDateFormat { + public static final String UTC = "UTC"; private static final long serialVersionUID = 3258415049197565235L; /**
 yyyy-MM-dd'T'HH:mm:ss 
*/ @@ -235,7 +234,7 @@ public class CachingDateFormat extends SimpleDateFormat } /** - * @return Returns a thread-safe formatter for the cmis sql datetime format + * @return Returns a thread-safe formatter for the Solr ISO 8601 datetime format */ public static SimpleDateFormat getSolrDatetimeFormat() { @@ -245,11 +244,13 @@ public class CachingDateFormat extends SimpleDateFormat } CachingDateFormat formatter = new CachingDateFormat(FORMAT_SOLR); - // it must be strict formatter.setLenient(false); - // put this into the threadlocal object + /* Apache Solr only supports the ISO 8601 date format: + * UTC and western locale are mandatory (only Arabic numerals (0123456789) are supported) */ + formatter.setTimeZone(TimeZone.getTimeZone(UTC)); + formatter.setNumberFormat(NumberFormat.getNumberInstance(Locale.ENGLISH)); + s_localSolrDatetime.set(formatter); - // done return s_localSolrDatetime.get(); } diff --git a/src/test/java/org/alfresco/util/CachingDateFormatTest.java b/src/test/java/org/alfresco/util/CachingDateFormatTest.java new file mode 100644 index 0000000000..ce203935e5 --- /dev/null +++ b/src/test/java/org/alfresco/util/CachingDateFormatTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2018 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.junit.After; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.Locale; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class CachingDateFormatTest +{ + private final LocalDateTime REFERENCE_DATE_TIME = LocalDateTime.of(2018, 4, 1, 10, 0); //2018-04-01 at 10:00am + private final Locale defaultLocale = Locale.getDefault(); + + @Test + public void solrDatetimeFormat_DateNotUTC_shouldReturnISO8601DateString() + { + Instant shanghaiInstant = REFERENCE_DATE_TIME.atZone(ZoneId.of("Asia/Shanghai")).toInstant(); + Date shanghaiDate = Date.from(shanghaiInstant); + SimpleDateFormat solrDatetimeFormat = CachingDateFormat.getSolrDatetimeFormat(); + + String formattedDate = solrDatetimeFormat.format(shanghaiDate); + + assertThat(formattedDate,is("2018-04-01T02:00:00.000Z")); + } + + @Test + public void solrDatetimeFormat_allLocales_shouldReturnISO8601DateString() + { + for(Locale currentLocale:Locale.getAvailableLocales()) + { + Locale.setDefault(currentLocale); + Instant utcInstant = REFERENCE_DATE_TIME.atZone(ZoneId.of("UTC")).toInstant(); + Date utcDate = Date.from(utcInstant); + SimpleDateFormat solrDatetimeFormat = CachingDateFormat.getSolrDatetimeFormat(); + + String formattedDate = solrDatetimeFormat.format(utcDate); + + assertThat(formattedDate, is("2018-04-01T10:00:00.000Z")); + } + } + + @After + public void tearDown() throws Exception + { + Locale.setDefault(defaultLocale); + } +} From dd341e81088e147bb98e02a6e58a0ea5de27254c Mon Sep 17 00:00:00 2001 From: Andrea Gazzarini Date: Tue, 27 Nov 2018 17:00:15 +0100 Subject: [PATCH 097/282] [ SEARCH-1286 ] additional ThreadLocal + date format (UTC - msecs) (#26) * [ SEARCH-1286 ] additional ThreadLocal + date format (UTC - msecs) * [ SEARCH-1286 ] ThreadLocal + other minor refactoring + unit tests * [ SEARCH-1286 ] Fixed Timezone issue in Unit tests * [ SEARCH-1286 ] Fixed Timezone issue II in Unit tests --- .../org/alfresco/util/CachingDateFormat.java | 298 ++++++++---------- .../alfresco/util/CachingDateFormatTest.java | 66 +++- 2 files changed, 192 insertions(+), 172 deletions(-) diff --git a/src/main/java/org/alfresco/util/CachingDateFormat.java b/src/main/java/org/alfresco/util/CachingDateFormat.java index 6232c4913f..446e9567ee 100644 --- a/src/main/java/org/alfresco/util/CachingDateFormat.java +++ b/src/main/java/org/alfresco/util/CachingDateFormat.java @@ -22,17 +22,13 @@ import java.text.NumberFormat; import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; -import java.util.WeakHashMap; +import java.util.*; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; +import static java.util.Arrays.stream; + /** * Provides thread safe means of obtaining a cached date formatter. *

@@ -41,88 +37,101 @@ import org.joda.time.format.ISODateTimeFormat; * @see java.text.DateFormat#setLenient(boolean) * * @author Derek Hulley + * @author Andrea Gazzarini */ public class CachingDateFormat extends SimpleDateFormat { - public static final String UTC = "UTC"; private static final long serialVersionUID = 3258415049197565235L; + public static final String UTC = "UTC"; - /**

 yyyy-MM-dd'T'HH:mm:ss 
*/ public static final String FORMAT_FULL_GENERIC = "yyyy-MM-dd'T'HH:mm:ss"; - - /**
 yyyy-MM-dd'T'HH:mm:ss 
*/ public static final String FORMAT_CMIS_SQL = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - public static final String FORMAT_SOLR = "yyyy-MM-dd'T'HH:mm:ss.SSSX"; - - public static final StringAndResolution[] LENIENT_FORMATS; - - - static - { - ArrayList list = new ArrayList (); - list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Calendar.MILLISECOND)); - list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss.SSS", Calendar.MILLISECOND)); - list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ssZ", Calendar.SECOND)); - list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss", Calendar.SECOND)); - list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mmZ", Calendar.MINUTE)); - list.add( new StringAndResolution("yyyy-MM-dd'T'HH:mm", Calendar.MINUTE)); - list.add( new StringAndResolution("yyyy-MM-dd'T'HHZ", Calendar.HOUR_OF_DAY)); - list.add( new StringAndResolution("yyyy-MM-dd'T'HH", Calendar.HOUR_OF_DAY)); - list.add( new StringAndResolution("yyyy-MM-dd'T'Z", Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution("yyyy-MM-dd'T'", Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution("yyyy-MM-ddZ", Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution("yyyy-MM-dd", Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution("yyyy-MMZ", Calendar.MONTH)); - list.add( new StringAndResolution("yyyy-MM", Calendar.MONTH)); - // year would duplicate :-) and eat stuff - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss.SSSZ", Calendar.MILLISECOND)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss.SSS", Calendar.MILLISECOND)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ssZ", Calendar.SECOND)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss", Calendar.SECOND)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mmZ", Calendar.MINUTE)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH:mm", Calendar.MINUTE)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HHZ", Calendar.HOUR_OF_DAY)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'HH", Calendar.HOUR_OF_DAY)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'Z",Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution( "yyyy-MMM-dd'T'",Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution( "yyyy-MMM-ddZ", Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution( "yyyy-MMM-dd", Calendar.DAY_OF_MONTH)); - list.add( new StringAndResolution( "yyyy-MMMZ", Calendar.MONTH)); - list.add( new StringAndResolution( "yyyy-MMM", Calendar.MONTH)); - list.add( new StringAndResolution("yyyyZ", Calendar.YEAR)); - list.add( new StringAndResolution("yyyy", Calendar.YEAR)); - - - - LENIENT_FORMATS = list.toArray(new StringAndResolution[]{}); - } - - /**
 yyyy-MM-dd 
*/ + public static final String UTC_WITHOUT_MSECS = "yyyy-MM-dd'T'HH:mm:ss'Z'"; public static final String FORMAT_DATE_GENERIC = "yyyy-MM-dd"; - - /**
 HH:mm:ss 
*/ public static final String FORMAT_TIME_GENERIC = "HH:mm:ss"; - private static ThreadLocal s_localDateFormat = new ThreadLocal(); - - private static ThreadLocal s_localDateOnlyFormat = new ThreadLocal(); - - private static ThreadLocal s_localTimeOnlyFormat = new ThreadLocal(); - - private static ThreadLocal s_localCmisSqlDatetime = new ThreadLocal(); - - private static ThreadLocal s_localSolrDatetime = new ThreadLocal(); - - private static ThreadLocal s_lenientParsers = new ThreadLocal(); - - transient private Map cacheDates = new WeakHashMap(89); - - private CachingDateFormat(String format) + public static final StringAndResolution[] LENIENT_FORMATS = { - super(format); + new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Calendar.MILLISECOND), + new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss.SSS", Calendar.MILLISECOND), + new StringAndResolution("yyyy-MM-dd'T'HH:mm:ssZ", Calendar.SECOND), + new StringAndResolution("yyyy-MM-dd'T'HH:mm:ss", Calendar.SECOND), + new StringAndResolution("yyyy-MM-dd'T'HH:mmZ", Calendar.MINUTE), + new StringAndResolution("yyyy-MM-dd'T'HH:mm", Calendar.MINUTE), + new StringAndResolution("yyyy-MM-dd'T'HHZ", Calendar.HOUR_OF_DAY), + new StringAndResolution("yyyy-MM-dd'T'HH", Calendar.HOUR_OF_DAY), + new StringAndResolution("yyyy-MM-dd'T'Z", Calendar.DAY_OF_MONTH), + new StringAndResolution("yyyy-MM-dd'T'", Calendar.DAY_OF_MONTH), + new StringAndResolution("yyyy-MM-ddZ", Calendar.DAY_OF_MONTH), + new StringAndResolution("yyyy-MM-dd", Calendar.DAY_OF_MONTH), + new StringAndResolution("yyyy-MMZ", Calendar.MONTH), + new StringAndResolution("yyyy-MM", Calendar.MONTH), + new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss.SSSZ", Calendar.MILLISECOND), + new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss.SSS", Calendar.MILLISECOND), + new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ssZ", Calendar.SECOND), + new StringAndResolution( "yyyy-MMM-dd'T'HH:mm:ss", Calendar.SECOND), + new StringAndResolution( "yyyy-MMM-dd'T'HH:mmZ", Calendar.MINUTE), + new StringAndResolution( "yyyy-MMM-dd'T'HH:mm", Calendar.MINUTE), + new StringAndResolution( "yyyy-MMM-dd'T'HHZ", Calendar.HOUR_OF_DAY), + new StringAndResolution( "yyyy-MMM-dd'T'HH", Calendar.HOUR_OF_DAY), + new StringAndResolution( "yyyy-MMM-dd'T'Z",Calendar.DAY_OF_MONTH), + new StringAndResolution( "yyyy-MMM-dd'T'",Calendar.DAY_OF_MONTH), + new StringAndResolution( "yyyy-MMM-ddZ", Calendar.DAY_OF_MONTH), + new StringAndResolution( "yyyy-MMM-dd", Calendar.DAY_OF_MONTH), + new StringAndResolution( "yyyy-MMMZ", Calendar.MONTH), + new StringAndResolution( "yyyy-MMM", Calendar.MONTH), + new StringAndResolution("yyyyZ", Calendar.YEAR), + new StringAndResolution("yyyy", Calendar.YEAR) + }; + + private static ThreadLocal S_LOCAL_DATE_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_FULL_GENERIC)); + + private static ThreadLocal S_LOCAL_DATEONLY_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_DATE_GENERIC)); + + private static ThreadLocal S_LOCAL_TIMEONLY_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_TIME_GENERIC)); + + private static ThreadLocal S_LOCAL_CMIS_SQL_DATETIME = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_CMIS_SQL)); + + private static ThreadLocal S_LOCAL_SOLR_DATETIME = ThreadLocal.withInitial(()-> + { + CachingDateFormat formatter = newDateFormat(FORMAT_SOLR); + /* + SEARCH-1263 + Apache Solr only supports the ISO 8601 date format: + UTC and western locale are mandatory (only Arabic numerals (0123456789) are supported) + */ + formatter.setTimeZone(TimeZone.getTimeZone(UTC)); + formatter.setNumberFormat(NumberFormat.getNumberInstance(Locale.ENGLISH)); + return formatter; + }); + + private static ThreadLocal S_UTC_DATETIME_WITHOUT_MSECS = ThreadLocal.withInitial(() -> + { + CachingDateFormat formatter = newDateFormat(UTC_WITHOUT_MSECS); + formatter.setTimeZone(TimeZone.getTimeZone(UTC)); + formatter.setNumberFormat(NumberFormat.getNumberInstance(Locale.ENGLISH)); + + return formatter; + }); + + private static ThreadLocal S_LENIENT_PARSERS = + ThreadLocal.withInitial(() -> + stream(LENIENT_FORMATS) + .map(format -> { + CachingDateFormat formatter = new CachingDateFormat(format.string); + formatter.setLenient(false); + return new SimpleDateFormatAndResolution(formatter, format.resolution); }) + .toArray(SimpleDateFormatAndResolution[]::new)); + + private Map cacheDates = new WeakHashMap<>(89); + + private CachingDateFormat(String pattern) + { + super(pattern); } + @Override public String toString() { return this.toPattern(); @@ -194,64 +203,47 @@ public class CachingDateFormat extends SimpleDateFormat } /** - * @return Returns a thread-safe formatter for the generic date/time format - * + * Returns a thread-safe formatter for the generic date/time format. + * * @see #FORMAT_FULL_GENERIC + * @return a thread-safe formatter for the generic date/time format. */ public static SimpleDateFormat getDateFormat() { - if (s_localDateFormat.get() != null) - { - return s_localDateFormat.get(); - } - - CachingDateFormat formatter = new CachingDateFormat(FORMAT_FULL_GENERIC); - // it must be strict - formatter.setLenient(false); - // put this into the threadlocal object - s_localDateFormat.set(formatter); - // done - return s_localDateFormat.get(); + return S_LOCAL_DATE_FORMAT.get(); } /** - * @return Returns a thread-safe formatter for the cmis sql datetime format + * Returns a thread-safe formatter for the cmis sql datetime format. + * + * @see #FORMAT_CMIS_SQL + * @return a thread-safe formatter for the cmis sql datetime format. */ public static SimpleDateFormat getCmisSqlDatetimeFormat() { - if (s_localCmisSqlDatetime.get() != null) - { - return s_localCmisSqlDatetime.get(); - } - - CachingDateFormat formatter = new CachingDateFormat(FORMAT_CMIS_SQL); - // it must be strict - formatter.setLenient(false); - // put this into the threadlocal object - s_localCmisSqlDatetime.set(formatter); - // done - return s_localCmisSqlDatetime.get(); + return S_LOCAL_CMIS_SQL_DATETIME.get(); } /** - * @return Returns a thread-safe formatter for the Solr ISO 8601 datetime format + * Returns a thread-safe formatter for the Solr ISO 8601 datetime format (without the msecs part). + * + * @see #UTC_WITHOUT_MSECS + * @return Returns a thread-safe formatter for the Solr ISO 8601 datetime format (without the msecs part). + */ + public static SimpleDateFormat getSolrDatetimeFormatWithoutMsecs() + { + return S_UTC_DATETIME_WITHOUT_MSECS.get(); + } + + /** + * Returns a thread-safe formatter for the Solr ISO 8601 datetime format. + * + * @see #FORMAT_SOLR + * @return a thread-safe formatter for the Solr ISO 8601 datetime format */ public static SimpleDateFormat getSolrDatetimeFormat() { - if (s_localSolrDatetime.get() != null) - { - return s_localSolrDatetime.get(); - } - - CachingDateFormat formatter = new CachingDateFormat(FORMAT_SOLR); - formatter.setLenient(false); - /* Apache Solr only supports the ISO 8601 date format: - * UTC and western locale are mandatory (only Arabic numerals (0123456789) are supported) */ - formatter.setTimeZone(TimeZone.getTimeZone(UTC)); - formatter.setNumberFormat(NumberFormat.getNumberInstance(Locale.ENGLISH)); - - s_localSolrDatetime.set(formatter); - return s_localSolrDatetime.get(); + return S_LOCAL_SOLR_DATETIME.get(); } /** @@ -261,39 +253,18 @@ public class CachingDateFormat extends SimpleDateFormat */ public static SimpleDateFormat getDateOnlyFormat() { - if (s_localDateOnlyFormat.get() != null) - { - return s_localDateOnlyFormat.get(); - } - - CachingDateFormat formatter = new CachingDateFormat(FORMAT_DATE_GENERIC); - // it must be strict - formatter.setLenient(false); - // put this into the threadlocal object - s_localDateOnlyFormat.set(formatter); - // done - return s_localDateOnlyFormat.get(); + return S_LOCAL_DATEONLY_FORMAT.get(); } /** - * @return Returns a thread-safe formatter for the generic time format - * + * Returns a thread-safe formatter for the generic time format. + * * @see #FORMAT_TIME_GENERIC + * @return a thread-safe formatter for the generic time format. */ public static SimpleDateFormat getTimeOnlyFormat() { - if (s_localTimeOnlyFormat.get() != null) - { - return s_localTimeOnlyFormat.get(); - } - - CachingDateFormat formatter = new CachingDateFormat(FORMAT_TIME_GENERIC); - // it must be strict - formatter.setLenient(false); - // put this into the threadlocal object - s_localTimeOnlyFormat.set(formatter); - // done - return s_localTimeOnlyFormat.get(); + return S_LOCAL_TIMEONLY_FORMAT.get(); } /** @@ -311,8 +282,7 @@ public class CachingDateFormat extends SimpleDateFormat if ((date != null) && (pos.getIndex() == text.length())) { cacheDates.put(text, date); - Date clonedDate = (Date) date.clone(); - return clonedDate; + return (Date) date.clone(); } else { @@ -322,8 +292,7 @@ public class CachingDateFormat extends SimpleDateFormat else { pos.setIndex(text.length()); - Date clonedDate = (Date) cached.clone(); - return clonedDate; + return (Date) cached.clone(); } } @@ -337,7 +306,7 @@ public class CachingDateFormat extends SimpleDateFormat } catch(IllegalArgumentException e) { - + // Nothing to be done here } SimpleDateFormatAndResolution[] formatters = getLenientFormatters(); @@ -362,25 +331,7 @@ public class CachingDateFormat extends SimpleDateFormat public static SimpleDateFormatAndResolution[] getLenientFormatters() { - if (s_lenientParsers.get() != null) - { - return s_lenientParsers.get(); - } - - int i = 0; - SimpleDateFormatAndResolution[] formatters = new SimpleDateFormatAndResolution[LENIENT_FORMATS.length]; - for(StringAndResolution format : LENIENT_FORMATS) - { - CachingDateFormat formatter = new CachingDateFormat(format.string); - // it must be strict - formatter.setLenient(false); - formatters[i++] = new SimpleDateFormatAndResolution(formatter, format.resolution); - } - - // put this into the threadlocal object - s_lenientParsers.set(formatters); - // done - return s_lenientParsers.get(); + return S_LENIENT_PARSERS.get(); } public static class StringAndResolution @@ -439,4 +390,17 @@ public class CachingDateFormat extends SimpleDateFormat } } + + /** + * Creates a new non-lenient {@link CachingDateFormat} instance. + * + * @param pattern the date / datetime pattern. + * @return new non-lenient {@link CachingDateFormat} instance. + */ + private static CachingDateFormat newDateFormat(String pattern) + { + CachingDateFormat formatter = new CachingDateFormat(pattern); + formatter.setLenient(false); + return formatter; + } } diff --git a/src/test/java/org/alfresco/util/CachingDateFormatTest.java b/src/test/java/org/alfresco/util/CachingDateFormatTest.java index ce203935e5..ecf964d8d0 100644 --- a/src/test/java/org/alfresco/util/CachingDateFormatTest.java +++ b/src/test/java/org/alfresco/util/CachingDateFormatTest.java @@ -27,9 +27,11 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; import java.util.Locale; +import java.util.TimeZone; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; public class CachingDateFormatTest { @@ -39,8 +41,7 @@ public class CachingDateFormatTest @Test public void solrDatetimeFormat_DateNotUTC_shouldReturnISO8601DateString() { - Instant shanghaiInstant = REFERENCE_DATE_TIME.atZone(ZoneId.of("Asia/Shanghai")).toInstant(); - Date shanghaiDate = Date.from(shanghaiInstant); + Date shanghaiDate = testDate("Asia/Shanghai"); SimpleDateFormat solrDatetimeFormat = CachingDateFormat.getSolrDatetimeFormat(); String formattedDate = solrDatetimeFormat.format(shanghaiDate); @@ -54,8 +55,8 @@ public class CachingDateFormatTest for(Locale currentLocale:Locale.getAvailableLocales()) { Locale.setDefault(currentLocale); - Instant utcInstant = REFERENCE_DATE_TIME.atZone(ZoneId.of("UTC")).toInstant(); - Date utcDate = Date.from(utcInstant); + + Date utcDate = testDate("UTC"); SimpleDateFormat solrDatetimeFormat = CachingDateFormat.getSolrDatetimeFormat(); String formattedDate = solrDatetimeFormat.format(utcDate); @@ -64,9 +65,64 @@ public class CachingDateFormatTest } } + @Test + public void onlyDateFormatReturnsOnlyTheDatePart() + { + Date utcDate = testDate("UTC"); + + SimpleDateFormat formatter = CachingDateFormat.getDateOnlyFormat(); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + + assertEquals("2018-04-01", formatter.format(utcDate)); + } + + @Test + public void onlyTimeFormatShouldReturnOnlyTheTimePart() + { + Date utcDate = testDate("UTC"); + + SimpleDateFormat formatter = CachingDateFormat.getTimeOnlyFormat(); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + + assertEquals("10:00:00", formatter.format(utcDate)); + } + + @Test + public void dateTimeFormatShouldReturnDateAndTime() + { + Date utcDate = testDate("UTC"); + + SimpleDateFormat formatter = CachingDateFormat.getDateFormat(); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + + assertEquals("2018-04-01T10:00:00", formatter.format(utcDate)); + } + + @Test + public void utcWithoutMsecsDatetimeFormat_shouldReturnStringsWithoutMsecs() + { + Date utcDate = testDate("UTC"); + + SimpleDateFormat formatter = CachingDateFormat.getSolrDatetimeFormatWithoutMsecs(); + + assertEquals("2018-04-01T10:00:00Z", formatter.format(utcDate)); + } + @After - public void tearDown() throws Exception + public void tearDown() { Locale.setDefault(defaultLocale); } + + /** + * Creates a test date using the given timezone id. + * + * @param zoneId the timezone id. + * @return a test date using the given timezone id. + */ + private Date testDate(String zoneId) + { + Instant utcInstant = REFERENCE_DATE_TIME.atZone(ZoneId.of(zoneId)).toInstant(); + return Date.from(utcInstant); + } } From 22c41ab479b2dd71f53a1e33aafbc6cbf5f660ed Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 28 Nov 2018 13:04:35 +0000 Subject: [PATCH 098/282] [maven-release-plugin] prepare release 7.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 82c228d9d8..9bc6414754 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.6-SNAPSHOT + 7.6 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.6 From 7ab552310a92bd995ec2d05d4a907eaa4bd88439 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Wed, 28 Nov 2018 13:04:42 +0000 Subject: [PATCH 099/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9bc6414754..c7a0a59739 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.6 + 7.7-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.6 + HEAD From 51077e8ad0757b98d979dbc0969cfc7052041e44 Mon Sep 17 00:00:00 2001 From: Andrea Gazzarini Date: Fri, 30 Nov 2018 12:06:37 +0100 Subject: [PATCH 100/282] SEARCH-1286 date type casting errors (#27) * [ SEARCH-1286 ] additional ThreadLocal + date format (UTC - msecs) * [ SEARCH-1286 ] ThreadLocal + other minor refactoring + unit tests * [ SEARCH-1286 ] Fixed Timezone issue in Unit tests * [ SEARCH-1286 ] Fixed Timezone issue II in Unit tests * [ SEARCH-1286 ] Fixed Locale issue in CachingDataFormat * Merge branches 'fix/SEARCH-1286_date_type_casting_errors' and 'master' of https://github.com/Alfresco/alfresco-core into fix/SEARCH-1286_date_type_casting_errors # Conflicts: # src/main/java/org/alfresco/util/CachingDateFormat.java # src/test/java/org/alfresco/util/CachingDateFormatTest.java --- .../org/alfresco/util/CachingDateFormat.java | 47 ++++++++++++------- .../alfresco/util/CachingDateFormatTest.java | 28 +++++++---- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/alfresco/util/CachingDateFormat.java b/src/main/java/org/alfresco/util/CachingDateFormat.java index 446e9567ee..c719c42276 100644 --- a/src/main/java/org/alfresco/util/CachingDateFormat.java +++ b/src/main/java/org/alfresco/util/CachingDateFormat.java @@ -18,7 +18,8 @@ */ package org.alfresco.util; -import java.text.NumberFormat; +import static java.util.Arrays.stream; + import java.text.ParseException; import java.text.ParsePosition; import java.text.SimpleDateFormat; @@ -27,8 +28,6 @@ import java.util.*; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; -import static java.util.Arrays.stream; - /** * Provides thread safe means of obtaining a cached date formatter. *

@@ -85,37 +84,34 @@ public class CachingDateFormat extends SimpleDateFormat new StringAndResolution("yyyy", Calendar.YEAR) }; - private static ThreadLocal S_LOCAL_DATE_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_FULL_GENERIC)); + static ThreadLocal S_LOCAL_DATE_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_FULL_GENERIC)); - private static ThreadLocal S_LOCAL_DATEONLY_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_DATE_GENERIC)); + static ThreadLocal S_LOCAL_DATEONLY_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_DATE_GENERIC)); - private static ThreadLocal S_LOCAL_TIMEONLY_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_TIME_GENERIC)); + static ThreadLocal S_LOCAL_TIMEONLY_FORMAT = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_TIME_GENERIC)); - private static ThreadLocal S_LOCAL_CMIS_SQL_DATETIME = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_CMIS_SQL)); + static ThreadLocal S_LOCAL_CMIS_SQL_DATETIME = ThreadLocal.withInitial(() -> newDateFormat(FORMAT_CMIS_SQL)); - private static ThreadLocal S_LOCAL_SOLR_DATETIME = ThreadLocal.withInitial(()-> + static ThreadLocal S_LOCAL_SOLR_DATETIME = ThreadLocal.withInitial(()-> { - CachingDateFormat formatter = newDateFormat(FORMAT_SOLR); + CachingDateFormat formatter = newDateFormatWithLocale(FORMAT_SOLR, Locale.ENGLISH); /* SEARCH-1263 Apache Solr only supports the ISO 8601 date format: UTC and western locale are mandatory (only Arabic numerals (0123456789) are supported) */ formatter.setTimeZone(TimeZone.getTimeZone(UTC)); - formatter.setNumberFormat(NumberFormat.getNumberInstance(Locale.ENGLISH)); return formatter; }); - private static ThreadLocal S_UTC_DATETIME_WITHOUT_MSECS = ThreadLocal.withInitial(() -> + static ThreadLocal S_UTC_DATETIME_WITHOUT_MSECS = ThreadLocal.withInitial(() -> { - CachingDateFormat formatter = newDateFormat(UTC_WITHOUT_MSECS); + CachingDateFormat formatter = newDateFormatWithLocale(UTC_WITHOUT_MSECS, Locale.ENGLISH); formatter.setTimeZone(TimeZone.getTimeZone(UTC)); - formatter.setNumberFormat(NumberFormat.getNumberInstance(Locale.ENGLISH)); - return formatter; }); - private static ThreadLocal S_LENIENT_PARSERS = + static ThreadLocal S_LENIENT_PARSERS = ThreadLocal.withInitial(() -> stream(LENIENT_FORMATS) .map(format -> { @@ -126,6 +122,11 @@ public class CachingDateFormat extends SimpleDateFormat private Map cacheDates = new WeakHashMap<>(89); + private CachingDateFormat(String pattern, Locale locale) + { + super(pattern, locale); + } + private CachingDateFormat(String pattern) { super(pattern); @@ -325,8 +326,6 @@ public class CachingDateFormat extends SimpleDateFormat } throw new ParseException("Unknown date format", 0); - - } public static SimpleDateFormatAndResolution[] getLenientFormatters() @@ -403,4 +402,18 @@ public class CachingDateFormat extends SimpleDateFormat formatter.setLenient(false); return formatter; } + + /** + * Creates a new non-lenient localised {@link CachingDateFormat} instance. + * + * @param pattern the date / datetime pattern. + * @param locale the locale. + * @return new non-lenient {@link CachingDateFormat} instance. + */ + private static CachingDateFormat newDateFormatWithLocale(String pattern, Locale locale) + { + CachingDateFormat formatter = new CachingDateFormat(pattern, locale); + formatter.setLenient(false); + return formatter; + } } diff --git a/src/test/java/org/alfresco/util/CachingDateFormatTest.java b/src/test/java/org/alfresco/util/CachingDateFormatTest.java index ecf964d8d0..dfd3096181 100644 --- a/src/test/java/org/alfresco/util/CachingDateFormatTest.java +++ b/src/test/java/org/alfresco/util/CachingDateFormatTest.java @@ -18,7 +18,12 @@ */ package org.alfresco.util; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + import org.junit.After; +import org.junit.Before; import org.junit.Test; import java.text.SimpleDateFormat; @@ -29,24 +34,26 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; - public class CachingDateFormatTest { private final LocalDateTime REFERENCE_DATE_TIME = LocalDateTime.of(2018, 4, 1, 10, 0); //2018-04-01 at 10:00am private final Locale defaultLocale = Locale.getDefault(); - + + @Before + public void setUp() + { + CachingDateFormat.S_LOCAL_SOLR_DATETIME.remove(); + } + @Test - public void solrDatetimeFormat_DateNotUTC_shouldReturnISO8601DateString() + public void solrDatetimeFormat_shouldFormatTheMinDate() { Date shanghaiDate = testDate("Asia/Shanghai"); - SimpleDateFormat solrDatetimeFormat = CachingDateFormat.getSolrDatetimeFormat(); - + SimpleDateFormat solrDatetimeFormat = CachingDateFormat.getSolrDatetimeFormatWithoutMsecs(); + String formattedDate = solrDatetimeFormat.format(shanghaiDate); - - assertThat(formattedDate,is("2018-04-01T02:00:00.000Z")); + + assertThat(formattedDate,is("2018-04-01T02:00:00Z")); } @Test @@ -54,6 +61,7 @@ public class CachingDateFormatTest { for(Locale currentLocale:Locale.getAvailableLocales()) { + CachingDateFormat.S_LOCAL_SOLR_DATETIME.remove(); Locale.setDefault(currentLocale); Date utcDate = testDate("UTC"); From 4123346a16f205afcb25ad5a62e159506e8bdeaa Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 30 Nov 2018 11:35:53 +0000 Subject: [PATCH 101/282] [maven-release-plugin] prepare release 7.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c7a0a59739..574fb77959 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.7-SNAPSHOT + 7.7 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.7 From bef6661ddfa49f2689841682a761374541625395 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 30 Nov 2018 11:35:59 +0000 Subject: [PATCH 102/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 574fb77959..818f0351c8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.7 + 7.8-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.7 + HEAD From c01225f035b1a9cc69c7604b8909b53c670b69fc Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 14 Jan 2019 15:42:31 +0000 Subject: [PATCH 103/282] SEARCH-1341 Update Spring from 5.0.4 to 5.1.3. (#29) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 818f0351c8..acd85aa26a 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ - 5.0.4.RELEASE + 5.1.3.RELEASE 6.8 From 63d279e57fcdc57d66c3f2b2613d02216a3d947d Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Mon, 14 Jan 2019 15:49:55 +0000 Subject: [PATCH 104/282] [maven-release-plugin] prepare release 7.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index acd85aa26a..bad7753821 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.8-SNAPSHOT + 7.8 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.8 From 963eef8d9c9d7c31f34d5e81a5e0147f4a6a31af Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Mon, 14 Jan 2019 15:50:02 +0000 Subject: [PATCH 105/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bad7753821..eac6db65bb 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.8 + 7.9-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.8 + HEAD From 939cfd1b7fa1ae4ec3ff848f3a2bb0a3ee3aae9b Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Fri, 18 Jan 2019 11:41:52 +0000 Subject: [PATCH 106/282] Add Travis config --- .travis.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..d9a7fb364b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +dist: trusty +sudo: required +language: java +jdk: + - openjdk11 + +cache: + directories: + - $HOME/.m2 + +install: travis_retry mvn install -DskipTests=true -B -V + +script: travis_retry mvn test \ No newline at end of file From 76e7c29344f9557d92a62a8316e9451fa8ee7440 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Fri, 18 Jan 2019 11:54:46 +0000 Subject: [PATCH 107/282] Add Travis build status --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 79d6eecb86..568730aee3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ### Alfresco Core +[![Build Status](https://travis-ci.com/Alfresco/alfresco-core.svg?branch=master)](https://travis-ci.com/Alfresco/alfresco-core) Alfresco Core is a library packaged as a jar file which is part of [Alfresco Content Services Repository](https://community.alfresco.com/docs/DOC-6385-project-overview-repository). The library contains the following: * Various helpers and utils @@ -37,4 +38,4 @@ The SNAPSHOT version of the artifact is **never** published. The history for older versions can be found in [Alfresco SVN](https://svn.alfresco.com/repos/alfresco-open-mirror/services/alfresco-core/) ### Contributing guide -Please use [this guide](CONTRIBUTING.md) to make a contribution to the project. \ No newline at end of file +Please use [this guide](CONTRIBUTING.md) to make a contribution to the project. From 750f3417be4fac7f44df3ec07dc00cbb7d850ac6 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Fri, 18 Jan 2019 11:55:21 +0000 Subject: [PATCH 108/282] Correct formatting in Readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 568730aee3..13462dd056 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ ### Alfresco Core [![Build Status](https://travis-ci.com/Alfresco/alfresco-core.svg?branch=master)](https://travis-ci.com/Alfresco/alfresco-core) + Alfresco Core is a library packaged as a jar file which is part of [Alfresco Content Services Repository](https://community.alfresco.com/docs/DOC-6385-project-overview-repository). The library contains the following: * Various helpers and utils From cdcd0480c0fe395f1163bf5b354a7fd6af8d3013 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 14:17:29 +0000 Subject: [PATCH 109/282] Bump maven-jar-plugin from 3.1.0 to 3.1.1 (#39) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eac6db65bb..ad6c1463a1 100644 --- a/pom.xml +++ b/pom.xml @@ -233,7 +233,7 @@ maven-jar-plugin - 3.1.0 + 3.1.1 From eb8083e62da8d6d88de786fe31a52a5b2c404a78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 14:18:04 +0000 Subject: [PATCH 110/282] Bump jaxb-impl from 2.3.1 to 2.3.2 (#38) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ad6c1463a1..33e3455592 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,7 @@ com.sun.xml.bind jaxb-impl - 2.3.1 + 2.3.2 com.sun.xml.bind From 089fc4f28be3d333f65bdec081f5d9059f223c1a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 14:42:54 +0000 Subject: [PATCH 111/282] Bump slf4j-log4j12 from 1.7.21 to 1.7.25 (#34) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33e3455592..9b465bcc3c 100644 --- a/pom.xml +++ b/pom.xml @@ -188,7 +188,7 @@ org.slf4j slf4j-log4j12 - 1.7.21 + 1.7.25 test From 60e16184fe3c43414c97223d578eb1c5870366cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 14:43:23 +0000 Subject: [PATCH 112/282] Bump spring-surf-core-configservice from 6.8 to 6.20 (#32) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9b465bcc3c..baf676e980 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 5.1.3.RELEASE - 6.8 + 6.20 From 46c07fba09e68a0d1ecd53a6322d8a4222875799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 14:43:57 +0000 Subject: [PATCH 113/282] Bump jaxb-xjc from 2.3.1 to 2.3.2 (#30) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index baf676e980..7d0cc8c053 100644 --- a/pom.xml +++ b/pom.xml @@ -138,7 +138,7 @@ com.sun.xml.bind jaxb-xjc - 2.3.1 + 2.3.2 com.sun.xml.bind From a6fbb0ab848d8d834dce6795ebf44f277b1fc9e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 14:44:14 +0000 Subject: [PATCH 114/282] Bump httpclient from 4.5.4 to 4.5.6 (#21) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7d0cc8c053..1022b5ee2b 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,7 @@ org.apache.httpcomponents httpclient - 4.5.4 + 4.5.6 From 02dc9e9ac6a7ac1df5812dd4454802aac78845ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 15:03:54 +0000 Subject: [PATCH 115/282] Bump dependency.spring.version from 5.1.3.RELEASE to 5.1.4.RELEASE (#31) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1022b5ee2b..a6c034c125 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ - 5.1.3.RELEASE + 5.1.4.RELEASE 6.20 From 066695ca56d5ef7733325a5dbbaa5ac7ce768dd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 16:11:16 +0000 Subject: [PATCH 116/282] Bump jta from 1.0.1b to 1.1 (#37) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a6c034c125..5c07cb1a50 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ javax.transaction jta - 1.0.1b + 1.1 joda-time From 0c84a590e859e28d98c1806f59cb5aea02c6a0ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Fri, 18 Jan 2019 16:28:31 +0000 Subject: [PATCH 117/282] Bump joda-time from 2.9.3 to 2.10.1 (#35) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c07cb1a50..4a4423c85d 100644 --- a/pom.xml +++ b/pom.xml @@ -168,7 +168,7 @@ joda-time joda-time - 2.9.3 + 2.10.1 org.apache.httpcomponents From f4af46ebfb8f069dc4e80a6a69faf6637a303707 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 18 Jan 2019 16:47:30 +0000 Subject: [PATCH 118/282] [maven-release-plugin] prepare release 7.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4a4423c85d..a87319f606 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.9-SNAPSHOT + 7.9 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.9 From 30dd800782708a5d2605de6bb90047f5ef7cfc32 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Fri, 18 Jan 2019 16:47:37 +0000 Subject: [PATCH 119/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a87319f606..660a8f0a78 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.9 + 7.10-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.9 + HEAD From 59b6b2ee0a65f6784fcced45b607a909cae2382e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 28 Jan 2019 09:38:42 +0000 Subject: [PATCH 120/282] Bump httpclient from 4.5.6 to 4.5.7 (#43) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 660a8f0a78..aa04b6bb4e 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,7 @@ org.apache.httpcomponents httpclient - 4.5.6 + 4.5.7 From e6fd73930eb56c004dfbcecfea0f0044c813f38c Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Sat, 9 Feb 2019 17:11:15 +0000 Subject: [PATCH 121/282] Remove unused dom4j dependency --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index aa04b6bb4e..63cf88c1e0 100644 --- a/pom.xml +++ b/pom.xml @@ -150,11 +150,6 @@ jaxb-core 2.3.0.1 - - dom4j - dom4j - 1.6.1 - org.codehaus.guessencoding guessencoding From 93d3acd2078bacd071db603c28560fbee4ae6efd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 14 Feb 2019 14:30:12 +0000 Subject: [PATCH 122/282] Bump commons-codec from 1.11 to 1.12 (#45) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 63cf88c1e0..e2334769c5 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ commons-codec commons-codec - 1.11 + 1.12 commons-collections From 9bd14b06fbdc547e4d499ab66b2f6b491dbc99fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Thu, 14 Feb 2019 17:02:29 +0000 Subject: [PATCH 123/282] Bump dependency.spring.version from 5.1.4.RELEASE to 5.1.5.RELEASE (#46) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2334769c5..4e6567fafc 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ - 5.1.4.RELEASE + 5.1.5.RELEASE 6.20 From c5a93bd7769a68ab7bb02b189f4437ae87e70e29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 19 Feb 2019 17:49:08 +0000 Subject: [PATCH 124/282] Bump slf4j-log4j12 from 1.7.25 to 1.7.26 (#47) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4e6567fafc..945cc37a95 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ org.slf4j slf4j-log4j12 - 1.7.25 + 1.7.26 test From 66c5322b43c0bfb388f93980f88b841fb9cf3d10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 26 Mar 2019 13:37:48 +0000 Subject: [PATCH 125/282] Bump quartz from 2.3.0 to 2.3.1 (#50) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 945cc37a95..4011c12b52 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ org.quartz-scheduler quartz - 2.3.0 + 2.3.1 From 4bb26886950b9046c381476bc4a7067a5d12f003 Mon Sep 17 00:00:00 2001 From: "whitesource-bolt-for-github[bot]" Date: Sat, 30 Mar 2019 10:29:56 +0000 Subject: [PATCH 126/282] Initial WhiteSource configuration file --- .whitesource | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .whitesource diff --git a/.whitesource b/.whitesource new file mode 100644 index 0000000000..f340c5defc --- /dev/null +++ b/.whitesource @@ -0,0 +1,8 @@ +########################################################## +#### WhiteSource Integration configuration file #### +########################################################## + +# Configuration # +#---------------# +ws.repo.scan=true +vulnerable.check.run.conclusion.level=failure From dad5ed9630a02501ebf3e2495799e46bf8ca75b2 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Sat, 30 Mar 2019 10:34:35 +0000 Subject: [PATCH 127/282] Add WhiteSource scan to Travis (#51) --- .travis.yml | 12 ++- .whitesource-fs-agent.config | 197 +++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 .whitesource-fs-agent.config diff --git a/.travis.yml b/.travis.yml index d9a7fb364b..1bdf8eab84 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,14 @@ cache: install: travis_retry mvn install -DskipTests=true -B -V -script: travis_retry mvn test \ No newline at end of file +matrix: + include: + - name: "Build and test" + script: travis_retry mvn test + - name: "WhiteSource scan" + if: fork = false AND branch = master + script: + # Download the latest version of WhiteSource FS Agent + - curl -LJO https://github.com/whitesource/fs-agent-distribution/raw/master/standAlone/whitesource-fs-agent.jar + # Run WhiteSource FS Agent + - java -jar whitesource-fs-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .whitesource-fs-agent.config \ No newline at end of file diff --git a/.whitesource-fs-agent.config b/.whitesource-fs-agent.config new file mode 100644 index 0000000000..0295cc8996 --- /dev/null +++ b/.whitesource-fs-agent.config @@ -0,0 +1,197 @@ +#################################################################### +# WhiteSource FS-Agent configuration file +#################################################################### +########################################## +# GENERAL SCAN MODE: Files and Package Managers +########################################## + +checkPolicies=true +forceCheckAllDependencies=true +forceUpdate=true +forceUpdate.failBuildOnPolicyViolation=true +offline=false +#ignoreSourceFiles=true +#ignoreCertificateCheck=  +#scanComment= +#updateInventory=false + +#projectPerFolder=true +#projectPerFolderIncludes= +#projectPerFolderExcludes= + +#wss.connectionTimeoutMinutes=60 +# Change the below URL to your WhiteSource server. +# Use the 'WhiteSource Server URL' which can be retrieved +# from your 'Profile' page on the 'Server URLs' panel. +# Then, add the '/agent' path to it. +wss.url=https://saas.whitesourcesoftware.com/agent + +#npm.resolveDependencies=false +#npm.ignoreSourceFiles=false +#npm.includeDevDependencies=true +#npm.runPreStep=true +#npm.ignoreNpmLsErrors=true +#npm.ignoreScripts=true +#npm.yarnProject=true +#npm.accessToken= + +#bower.resolveDependencies=false +#bower.ignoreSourceFiles=true +#bower.runPreStep=true + +#nuget.resolvePackagesConfigFiles=false +#nuget.resolveCsProjFiles=false +#nuget.resolveDependencies=false +#nuget.restoreDependencies=true +#nuget.ignoreSourceFiles=true +#nuget.runPreStep=true + +#python.resolveDependencies=false +#python.ignoreSourceFiles=false +#python.ignorePipInstallErrors=true +#python.installVirtualenv=true +#python.resolveHierarchyTree=false +#python.requirementsFileIncludes=requirements.txt +#python.resolveSetupPyFiles=true +#python.runPipenvPreStep=true +#python.pipenvDevDependencies=true +#python.IgnorePipenvInstallErrors=true + +#maven.ignoredScopes=test provided +maven.resolveDependencies=true +#maven.ignoreSourceFiles=true +#maven.aggregateModules=true +maven.ignorePomModules=false +#maven.runPreStep=true +#maven.ignoreMvnTreeErrors=true + +#gradle.ignoredScopes= +#gradle.resolveDependencies=false +#gradle.runAssembleCommand=false +#gradle.runPreStep=true +#gradle.ignoreSourceFiles=true +#gradle.aggregateModules=true +#gradle.preferredEnvironment=wrapper +#gradle.runPreStep=true + +#paket.resolveDependencies=false +#paket.ignoredGroups= +#paket.ignoreSourceFiles=false +#paket.runPreStep=true +#paket.exePath= + +#go.resolveDependencies=false +#go.collectDependenciesAtRuntime=true +#go.dependencyManager= +#go.ignoreSourceFiles=true +#go.glide.ignoreTestPackages=false +#go.gogradle.enableTaskAlias=true + +#ruby.resolveDependencies = false +#ruby.ignoreSourceFiles = false +#ruby.installMissingGems = true +#ruby.runBundleInstall = true +#ruby.overwriteGemFile = true + +#sbt.resolveDependencies=false +#sbt.ignoreSourceFiles=true +#sbt.aggregateModules=true +#sbt.runPreStep=true +#sbt.targetFolder= + +#php.resolveDependencies=false +#php.runPreStep=true +#php.includeDevDependencies=true + +#html.resolveDependencies=false + +#cocoapods.resolveDependencies=false +#cocoapods.runPreStep=true +#cocoapods.ignoreSourceFiles=false + +################################## +# Organization tokens: +################################## +apiKey= + +#userKey is required if WhiteSource administrator has enabled "Enforce user level access" option +#userKey= + +projectName=alfresco-core +projectVersion= +projectToken= + +productName=ACS Community +productVersion= +productToken= +#updateType=APPEND +#requesterEmail=user@provider.com + +######################################################################################### +# Includes/Excludes Glob patterns - PLEASE USE ONLY ONE EXCLUDE LINE AND ONE INCLUDE LINE +######################################################################################### +#includes=**/*.c **/*.cc **/*.cp **/*.cpp **/*.cxx **/*.c++ **/*.h **/*.hpp **/*.hxx + +#includes=**/*.m **/*.mm **/*.js **/*.php +includes=**/*.jar +#includes=**/*.gem **/*.rb +#includes=**/*.dll **/*.cs **/*.nupkg +#includes=**/*.tgz **/*.deb **/*.gzip **/*.rpm **/*.tar.bz2 +#includes=**/*.zip **/*.tar.gz **/*.egg **/*.whl **/*.py + +## Exclude file extensions or specific directories by adding **/*. or **/** +excludes=**/*sources.jar **/*javadoc.jar + +case.sensitive.glob=false +followSymbolicLinks=true + +################################## +# Archive Properties +################################## +#archiveExtractionDepth=2 +#archiveIncludes=**/*.war **/*.ear +#archiveExcludes=**/*sources.jar + +################################## +# Proxy settings +################################## +#proxy.host= +#proxy.port= +#proxy.user= +#proxy.pass= + +################################## +# SCM settings +################################## +#scm.type= +#scm.user= +#scm.pass= +#scm.ppk= +#scm.url= +#scm.branch= +#scm.tag= +#scm.npmInstall= +#scm.npmInstallTimeoutMinutes= +#scm.repositoriesFile= + +############################################## +# SCAN MODE: Linux package manager settings +############################################## +#scanPackageManager=true + +################################## +# SCAN MODE: Docker images +################################## +#docker.includes=.*.* +#docker.excludes=.*.* +#docker.scanImages=true +#docker.pull.enable=true +#docker.pull.images=.*.* +#docker.pull.maxImages=10 +#docker.pull.tags=.*.* +#docker.pull.digest= +#docker.delete.force=true +#docker.login.sudo=false + +#docker.aws.enable=true +#docker.aws.registryIds= From e409842dd87eeb8d3f13af7d11e9b68f4faac7de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 30 Mar 2019 10:35:21 +0000 Subject: [PATCH 128/282] Bump spring-surf-core-configservice from 6.20 to 6.21 (#49) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4011c12b52..cdbe3bf343 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 5.1.5.RELEASE - 6.20 + 6.21 From 2eb2c9449f772d31cb19dc92cb7d0fa8b8648505 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 1 Apr 2019 09:46:02 +0000 Subject: [PATCH 129/282] Bump httpclient from 4.5.7 to 4.5.8 (#52) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cdbe3bf343..e242683308 100644 --- a/pom.xml +++ b/pom.xml @@ -168,7 +168,7 @@ org.apache.httpcomponents httpclient - 4.5.7 + 4.5.8 From 9e6f6d9456313a4801f5f0be65c888b8097ffd2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Mon, 1 Apr 2019 10:19:25 +0000 Subject: [PATCH 130/282] Bump dependency.spring.version from 5.1.5.RELEASE to 5.1.6.RELEASE (#53) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e242683308..060aff8191 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ - 5.1.5.RELEASE + 5.1.6.RELEASE 6.21 From b97ef572d92fd76166d0b2ae9a0666fec57bd2a9 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Fri, 5 Apr 2019 00:29:22 +0100 Subject: [PATCH 131/282] Avoid unnecessary travis runs --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1bdf8eab84..fb05ba5813 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,11 @@ cache: directories: - $HOME/.m2 +branches: + only: + - master + - /support\/.*/ + install: travis_retry mvn install -DskipTests=true -B -V matrix: @@ -15,7 +20,8 @@ matrix: - name: "Build and test" script: travis_retry mvn test - name: "WhiteSource scan" - if: fork = false AND branch = master + # only on master and if it is not a PR + if: fork = false AND branch = master AND type != pull_request script: # Download the latest version of WhiteSource FS Agent - curl -LJO https://github.com/whitesource/fs-agent-distribution/raw/master/standAlone/whitesource-fs-agent.jar From 1f7ca71543fe1e11f2f8977c32efd361481281a1 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Sun, 7 Apr 2019 15:43:41 +0100 Subject: [PATCH 132/282] Update surf-webscripts to 7.0 (#57) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 060aff8191..c63dfde16c 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 5.1.6.RELEASE - 6.21 + 7.0 From d391e47aed65ceb2d428a0311d080a2c2a87f6af Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Sun, 7 Apr 2019 14:49:57 +0000 Subject: [PATCH 133/282] [maven-release-plugin] prepare release 7.10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c63dfde16c..7fae1034f4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.10-SNAPSHOT + 7.10 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.10 From 3dde784a464947534613a3bf14936f293fac07b0 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Sun, 7 Apr 2019 14:50:06 +0000 Subject: [PATCH 134/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7fae1034f4..1577ec7f63 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.10 + 7.11-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.10 + HEAD From ca2089033d15ec64c1a2770a3f837f1f8c295c32 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Fri, 12 Apr 2019 14:38:46 +0100 Subject: [PATCH 135/282] Changed the old whitesource fs agent to unified (#59) --- .travis.yml | 8 +- ...-agent.config => .wss-unified-agent.config | 103 ++++++++++++------ 2 files changed, 71 insertions(+), 40 deletions(-) rename .whitesource-fs-agent.config => .wss-unified-agent.config (82%) diff --git a/.travis.yml b/.travis.yml index fb05ba5813..2c705fc7af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: # only on master and if it is not a PR if: fork = false AND branch = master AND type != pull_request script: - # Download the latest version of WhiteSource FS Agent - - curl -LJO https://github.com/whitesource/fs-agent-distribution/raw/master/standAlone/whitesource-fs-agent.jar - # Run WhiteSource FS Agent - - java -jar whitesource-fs-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .whitesource-fs-agent.config \ No newline at end of file + # Download the latest version of WhiteSource Unified Agent + - curl -LJO https://github.com/whitesource/unified-agent-distribution/raw/master/standAlone/wss-unified-agent.jar + # Run WhiteSource Unified Agent + - java -jar wss-unified-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .wss-unified-agent.config \ No newline at end of file diff --git a/.whitesource-fs-agent.config b/.wss-unified-agent.config similarity index 82% rename from .whitesource-fs-agent.config rename to .wss-unified-agent.config index 0295cc8996..6f9cb57795 100644 --- a/.whitesource-fs-agent.config +++ b/.wss-unified-agent.config @@ -1,31 +1,36 @@ #################################################################### -# WhiteSource FS-Agent configuration file +# WhiteSource Unified-Agent configuration file #################################################################### ########################################## # GENERAL SCAN MODE: Files and Package Managers ########################################## - + checkPolicies=true forceCheckAllDependencies=true forceUpdate=true forceUpdate.failBuildOnPolicyViolation=true offline=false #ignoreSourceFiles=true -#ignoreCertificateCheck=  #scanComment= #updateInventory=false - +#resolveAllDependencies=false +#failErrorLevel=ALL +#requireKnownSha1=false +#generateScanReport=true +#scanReportTimeoutMinutes=10 +#excludeDependenciesFromNodes=.*commons-io.*,.*maven-model + #projectPerFolder=true #projectPerFolderIncludes= #projectPerFolderExcludes= - + #wss.connectionTimeoutMinutes=60 # Change the below URL to your WhiteSource server. # Use the 'WhiteSource Server URL' which can be retrieved # from your 'Profile' page on the 'Server URLs' panel. # Then, add the '/agent' path to it. wss.url=https://saas.whitesourcesoftware.com/agent - + #npm.resolveDependencies=false #npm.ignoreSourceFiles=false #npm.includeDevDependencies=true @@ -34,18 +39,20 @@ wss.url=https://saas.whitesourcesoftware.com/agent #npm.ignoreScripts=true #npm.yarnProject=true #npm.accessToken= - +#npm.identifyByNameAndVersion=true + #bower.resolveDependencies=false #bower.ignoreSourceFiles=true #bower.runPreStep=true - + #nuget.resolvePackagesConfigFiles=false #nuget.resolveCsProjFiles=false #nuget.resolveDependencies=false #nuget.restoreDependencies=true #nuget.ignoreSourceFiles=true #nuget.runPreStep=true - +#nuget.resolveNuspecFiles=false + #python.resolveDependencies=false #python.ignoreSourceFiles=false #python.ignorePipInstallErrors=true @@ -56,7 +63,7 @@ wss.url=https://saas.whitesourcesoftware.com/agent #python.runPipenvPreStep=true #python.pipenvDevDependencies=true #python.IgnorePipenvInstallErrors=true - + #maven.ignoredScopes=test provided maven.resolveDependencies=true #maven.ignoreSourceFiles=true @@ -64,7 +71,9 @@ maven.resolveDependencies=true maven.ignorePomModules=false #maven.runPreStep=true #maven.ignoreMvnTreeErrors=true - +#maven.environmentPath= +#maven.m2RepositoryPath= + #gradle.ignoredScopes= #gradle.resolveDependencies=false #gradle.runAssembleCommand=false @@ -72,86 +81,91 @@ maven.ignorePomModules=false #gradle.ignoreSourceFiles=true #gradle.aggregateModules=true #gradle.preferredEnvironment=wrapper -#gradle.runPreStep=true - +#gradle.localRepositoryPath= + #paket.resolveDependencies=false #paket.ignoredGroups= #paket.ignoreSourceFiles=false #paket.runPreStep=true #paket.exePath= - + #go.resolveDependencies=false #go.collectDependenciesAtRuntime=true #go.dependencyManager= #go.ignoreSourceFiles=true #go.glide.ignoreTestPackages=false #go.gogradle.enableTaskAlias=true - + #ruby.resolveDependencies = false #ruby.ignoreSourceFiles = false #ruby.installMissingGems = true #ruby.runBundleInstall = true #ruby.overwriteGemFile = true - + #sbt.resolveDependencies=false #sbt.ignoreSourceFiles=true #sbt.aggregateModules=true #sbt.runPreStep=true #sbt.targetFolder= - + #php.resolveDependencies=false #php.runPreStep=true #php.includeDevDependencies=true - + #html.resolveDependencies=false - + #cocoapods.resolveDependencies=false #cocoapods.runPreStep=true #cocoapods.ignoreSourceFiles=false - + +#hex.resolveDependencies=false +#hex.runPreStep=true +#hex.ignoreSourceFiles=false +#hex.aggregateModules=true + ################################## # Organization tokens: ################################## apiKey= - + #userKey is required if WhiteSource administrator has enabled "Enforce user level access" option #userKey= - + projectName=alfresco-core projectVersion= projectToken= - + productName=ACS Community productVersion= productToken= #updateType=APPEND #requesterEmail=user@provider.com - + ######################################################################################### # Includes/Excludes Glob patterns - PLEASE USE ONLY ONE EXCLUDE LINE AND ONE INCLUDE LINE ######################################################################################### #includes=**/*.c **/*.cc **/*.cp **/*.cpp **/*.cxx **/*.c++ **/*.h **/*.hpp **/*.hxx - + #includes=**/*.m **/*.mm **/*.js **/*.php includes=**/*.jar #includes=**/*.gem **/*.rb #includes=**/*.dll **/*.cs **/*.nupkg #includes=**/*.tgz **/*.deb **/*.gzip **/*.rpm **/*.tar.bz2 #includes=**/*.zip **/*.tar.gz **/*.egg **/*.whl **/*.py - + ## Exclude file extensions or specific directories by adding **/*. or **/** excludes=**/*sources.jar **/*javadoc.jar - + case.sensitive.glob=false followSymbolicLinks=true - + ################################## -# Archive Properties +# Archive properties ################################## #archiveExtractionDepth=2 #archiveIncludes=**/*.war **/*.ear #archiveExcludes=**/*sources.jar - + ################################## # Proxy settings ################################## @@ -159,7 +173,7 @@ followSymbolicLinks=true #proxy.port= #proxy.user= #proxy.pass= - + ################################## # SCM settings ################################## @@ -173,18 +187,18 @@ followSymbolicLinks=true #scm.npmInstall= #scm.npmInstallTimeoutMinutes= #scm.repositoriesFile= - + ############################################## # SCAN MODE: Linux package manager settings ############################################## #scanPackageManager=true - + ################################## # SCAN MODE: Docker images ################################## -#docker.includes=.*.* -#docker.excludes=.*.* #docker.scanImages=true +#docker.includes=.*.* +#docker.excludes= #docker.pull.enable=true #docker.pull.images=.*.* #docker.pull.maxImages=10 @@ -192,6 +206,23 @@ followSymbolicLinks=true #docker.pull.digest= #docker.delete.force=true #docker.login.sudo=false - + #docker.aws.enable=true #docker.aws.registryIds= + +################################## +# SCAN MODE: Docker containers +################################## +#docker.scanContainers=true +#docker.containerIncludes=.*.* +#docker.containerExcludes= + +################################ +# Serverless settings +################################ +#serverless.provider= +#serverless.scanFunctions=true +#serverless.includes= +#serverless.excludes= +#serverless.region= +#serverless.maxFunctions=10 From c44d2292366faf9b6f881d7ff43ac5f326469149 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Fri, 12 Apr 2019 20:11:31 +0100 Subject: [PATCH 136/282] Limit WhiteSource scan to support branches or master --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c705fc7af..84acc903ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,8 @@ matrix: - name: "Build and test" script: travis_retry mvn test - name: "WhiteSource scan" - # only on master and if it is not a PR - if: fork = false AND branch = master AND type != pull_request + # only on support branches or master and if it is not a PR + if: fork = false AND (branch = master OR branch =~ /support\/.*/) AND type != pull_request script: # Download the latest version of WhiteSource Unified Agent - curl -LJO https://github.com/whitesource/unified-agent-distribution/raw/master/standAlone/wss-unified-agent.jar From 4cff2330401384075be2c0c3ab3c7862414fb533 Mon Sep 17 00:00:00 2001 From: "whitesource-bolt-for-github[bot]" Date: Fri, 10 May 2019 00:14:03 +0000 Subject: [PATCH 137/282] Migrate WhiteSource configuration file to json format --- .whitesource | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.whitesource b/.whitesource index f340c5defc..f056952141 100644 --- a/.whitesource +++ b/.whitesource @@ -1,8 +1,8 @@ -########################################################## -#### WhiteSource Integration configuration file #### -########################################################## - -# Configuration # -#---------------# -ws.repo.scan=true -vulnerable.check.run.conclusion.level=failure +{ + "generalSettings": { + "shouldScanRepo": true + }, + "checkRunSettings": { + "vulnerableCheckRunConclusionLevel": "failure" + } +} \ No newline at end of file From b12d82f9117f59438816d46a4e2d3a41e01ebad5 Mon Sep 17 00:00:00 2001 From: Martin Muller Date: Fri, 10 May 2019 10:19:43 +0100 Subject: [PATCH 138/282] REPO-4388 bump webscript --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1577ec7f63..8b4a9de430 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 5.1.6.RELEASE - 7.0 + 7.1 From 75e1ba01cf091724a92d3faf447c5693720e9c42 Mon Sep 17 00:00:00 2001 From: Angel Borroy <48685308+aborroy@users.noreply.github.com> Date: Thu, 23 May 2019 12:05:40 +0200 Subject: [PATCH 139/282] Using default SSL Port (443) for HttpClientFactory, as otherwise "getHostURL" method skips port information. (#63) --- src/main/java/org/alfresco/httpclient/HttpClientFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/alfresco/httpclient/HttpClientFactory.java b/src/main/java/org/alfresco/httpclient/HttpClientFactory.java index 1e4054f670..31356cb7aa 100644 --- a/src/main/java/org/alfresco/httpclient/HttpClientFactory.java +++ b/src/main/java/org/alfresco/httpclient/HttpClientFactory.java @@ -47,6 +47,7 @@ import org.apache.commons.httpclient.HttpHost; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.HttpVersion; +import org.apache.commons.httpclient.HttpsURL; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.SimpleHttpConnectionManager; import org.apache.commons.httpclient.URI; @@ -287,7 +288,8 @@ public class HttpClientFactory { // Configure a custom SSL socket factory that will enforce mutual authentication HttpClient httpClient = constructHttpClient(); - HttpHostFactory hostFactory = new HttpHostFactory(new Protocol("https", sslSocketFactory, httpsPort)); + // Default port is 443 for the HostFactory, when including customised port (like 8983) the port name is skipped from "getHostURL" string + HttpHostFactory hostFactory = new HttpHostFactory(new Protocol("https", sslSocketFactory, HttpsURL.DEFAULT_PORT)); httpClient.setHostConfiguration(new HostConfigurationWithHostFactory(hostFactory)); httpClient.getHostConfiguration().setHost(httpsHost, httpsPort, "https"); return httpClient; From d7cb91aa1917bc454a32d6a104a1c3c54fe671c7 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Mon, 27 May 2019 08:32:52 +0000 Subject: [PATCH 140/282] [maven-release-plugin] prepare release 7.11 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8b4a9de430..ccf894925e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.11-SNAPSHOT + 7.11 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.11 From 163c4fcc7cd93ed62362c07d65ff3c293806f277 Mon Sep 17 00:00:00 2001 From: alfresco-build Date: Mon, 27 May 2019 08:32:59 +0000 Subject: [PATCH 141/282] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ccf894925e..ee384d350f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.11 + 7.12-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.11 + HEAD From e1c19fefac4bfe278c4ad25be746d96929904538 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Wed, 5 Jun 2019 19:40:49 +0100 Subject: [PATCH 142/282] Add release configuration in Travis (#65) --- .travis.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 84acc903ee..0c4909b175 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,14 @@ branches: install: travis_retry mvn install -DskipTests=true -B -V -matrix: +stages: + - test + - release + +jobs: include: - - name: "Build and test" + - stage: test + name: "Build and test" script: travis_retry mvn test - name: "WhiteSource scan" # only on support branches or master and if it is not a PR @@ -26,4 +31,14 @@ matrix: # Download the latest version of WhiteSource Unified Agent - curl -LJO https://github.com/whitesource/unified-agent-distribution/raw/master/standAlone/wss-unified-agent.jar # Run WhiteSource Unified Agent - - java -jar wss-unified-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .wss-unified-agent.config \ No newline at end of file + - java -jar wss-unified-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .wss-unified-agent.config + - stage: release + name: "Push to Nexus" + if: fork = false AND branch = master AND type != pull_request AND commit_message !~ /\[no-release\]/ + before_install: + - "cp .travis.settings.xml $HOME/.m2/settings.xml" + script: + # Use full history for release + - git checkout -B "${TRAVIS_BRANCH}" + # Skip building of release commits + - mvn --batch-mode -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform \ No newline at end of file From 52dbae6cf27b1ba1ab7106d7adb3ef429b29f2ae Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Wed, 5 Jun 2019 19:45:41 +0100 Subject: [PATCH 143/282] Add maven settings --- .travis.settings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.settings.xml diff --git a/.travis.settings.xml b/.travis.settings.xml new file mode 100644 index 0000000000..dffe7fbfe1 --- /dev/null +++ b/.travis.settings.xml @@ -0,0 +1,10 @@ + + + + + alfresco-public + ${env.MAVEN_USERNAME} + ${env.MAVEN_PASSWORD} + + + From 4240e83b75f9be39107576e88bdd2a94f435bcd6 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 5 Jun 2019 18:50:41 +0000 Subject: [PATCH 144/282] [maven-release-plugin][skip ci] prepare release 7.12 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ee384d350f..4bcdafb99d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.12-SNAPSHOT + 7.12 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.12 From d578a6d0c4de40d27fc48bf7d878d9246bc519a1 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 5 Jun 2019 18:50:46 +0000 Subject: [PATCH 145/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4bcdafb99d..7bb9cd7a59 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.12 + 7.13-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.12 + HEAD From 4722c2db21c0b62a7ef58ecbc55109b96fc0a691 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Wed, 5 Jun 2019 20:06:50 +0100 Subject: [PATCH 146/282] Specify the java version in pom --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 7bb9cd7a59..ef1f09cfdc 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ 5.1.6.RELEASE 7.1 + 11 From 9e22bf24da15a6ce530e3f07a4fe3f15fe453bec Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 5 Jun 2019 19:12:41 +0000 Subject: [PATCH 147/282] [maven-release-plugin][skip ci] prepare release 7.13 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ef1f09cfdc..bc87150c74 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.13-SNAPSHOT + 7.13 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.13 From 333a5a1b9b2617e29dd7c85f780cb59b1120ee08 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 5 Jun 2019 19:12:47 +0000 Subject: [PATCH 148/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bc87150c74..0ca6c2c690 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.13 + 7.14-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.13 + HEAD From aa30fb4b6a13003e08dbb26a1720137d182d6191 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Wed, 5 Jun 2019 20:32:25 +0100 Subject: [PATCH 149/282] Reduce verbocity of release [ci skip] --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0c4909b175..4bf0d36dd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,4 +41,4 @@ jobs: # Use full history for release - git checkout -B "${TRAVIS_BRANCH}" # Skip building of release commits - - mvn --batch-mode -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform \ No newline at end of file + - mvn --batch-mode -q -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform \ No newline at end of file From 0c7c60c69a2696dd3065ea41c6d434a1c95e4a8e Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Wed, 5 Jun 2019 20:35:00 +0100 Subject: [PATCH 150/282] Fix distribution repository ID --- pom.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 0ca6c2c690..c518e8fbba 100644 --- a/pom.xml +++ b/pom.xml @@ -20,13 +20,9 @@ - alfresco-internal + alfresco-public https://artifacts.alfresco.com/nexus/content/repositories/releases - - alfresco-internal-snapshots - https://artifacts.alfresco.com/nexus/content/repositories/snapshots - From ec98de848fb2e1e5e062616a818d8e8891ebfada Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 5 Jun 2019 19:39:20 +0000 Subject: [PATCH 151/282] [maven-release-plugin][skip ci] prepare release 7.14 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c518e8fbba..1ac9f56012 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.14-SNAPSHOT + 7.14 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.14 From dabd638c728136f7cadd601ef8adb6734713f048 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 5 Jun 2019 19:39:25 +0000 Subject: [PATCH 152/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1ac9f56012..83171de516 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.14 + 7.15-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.14 + HEAD From 5d5551d065c6c02019d41507a22b54bd54a83cf3 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Wed, 5 Jun 2019 21:44:38 +0100 Subject: [PATCH 153/282] Limit whitesource scan to SP or master [skip ci] --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4bf0d36dd4..0c555de643 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,8 @@ jobs: name: "Build and test" script: travis_retry mvn test - name: "WhiteSource scan" - # only on support branches or master and if it is not a PR - if: fork = false AND (branch = master OR branch =~ /support\/.*/) AND type != pull_request + # only on SP branches or master and if it is not a PR + if: fork = false AND (branch = master OR branch =~ /support\/SP\/.*/) AND type != pull_request script: # Download the latest version of WhiteSource Unified Agent - curl -LJO https://github.com/whitesource/unified-agent-distribution/raw/master/standAlone/wss-unified-agent.jar From ccf23e9836f04b8c76df4b403408fbbb200a9c24 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Wed, 5 Jun 2019 21:45:53 +0100 Subject: [PATCH 154/282] Limit releases to support/* or master [skip ci] --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0c555de643..46442d410c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ jobs: - java -jar wss-unified-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .wss-unified-agent.config - stage: release name: "Push to Nexus" - if: fork = false AND branch = master AND type != pull_request AND commit_message !~ /\[no-release\]/ + if: fork = false AND (branch = master OR branch =~ /support\/.*/) AND type != pull_request AND commit_message !~ /\[no-release\]/ before_install: - "cp .travis.settings.xml $HOME/.m2/settings.xml" script: From 6a2e270a1020c51e0306150a764f6e4342c37992 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 13 Jun 2019 15:42:34 +0100 Subject: [PATCH 155/282] Bump httpclient from 4.5.8 to 4.5.9 (#67) Bumps httpclient from 4.5.8 to 4.5.9. Signed-off-by: dependabot-preview[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83171de516..558800e1d0 100644 --- a/pom.xml +++ b/pom.xml @@ -165,7 +165,7 @@ org.apache.httpcomponents httpclient - 4.5.8 + 4.5.9 From 4bab252b53a030b53dadd46dd288752f42a16194 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 13 Jun 2019 14:50:13 +0000 Subject: [PATCH 156/282] [maven-release-plugin][skip ci] prepare release 7.15 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 558800e1d0..b79e3c57db 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.15-SNAPSHOT + 7.15 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.15 From ad575afaf06d0a2cb0f474410e893de8f0d6381f Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 13 Jun 2019 14:50:20 +0000 Subject: [PATCH 157/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b79e3c57db..a407b25a6b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.15 + 7.16-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.15 + HEAD From f78fbf039a61841f53b243768c73f29121e7d731 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 13 Jun 2019 16:04:25 +0100 Subject: [PATCH 158/282] Bump spring-surf-core-configservice from 7.1 to 7.2 (#64) Bumps spring-surf-core-configservice from 7.1 to 7.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a407b25a6b..12cea71a7c 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 5.1.6.RELEASE - 7.1 + 7.2 11 From 8149bedd627a7794665f292abf5d800c3681ee77 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 13 Jun 2019 16:06:38 +0100 Subject: [PATCH 159/282] Bump maven-jar-plugin from 3.1.1 to 3.1.2 (#62) Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/apache/maven-jar-plugin/releases) - [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.1.1...maven-jar-plugin-3.1.2) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 12cea71a7c..d24a945aaf 100644 --- a/pom.xml +++ b/pom.xml @@ -225,7 +225,7 @@ maven-jar-plugin - 3.1.1 + 3.1.2 From 0060bd36027295e028ae87a3cb34a3cdfbe4fbe2 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Thu, 13 Jun 2019 16:07:55 +0100 Subject: [PATCH 160/282] Bump joda-time from 2.10.1 to 2.10.2 (#60) Bumps [joda-time](https://github.com/JodaOrg/joda-time) from 2.10.1 to 2.10.2. - [Release notes](https://github.com/JodaOrg/joda-time/releases) - [Changelog](https://github.com/JodaOrg/joda-time/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/JodaOrg/joda-time/compare/v2.10.1...v2.10.2) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d24a945aaf..00ea421da6 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ joda-time joda-time - 2.10.1 + 2.10.2 org.apache.httpcomponents From 7676a6a20bf562545873f3bfe31e5c3fb9891549 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 13 Jun 2019 15:37:32 +0000 Subject: [PATCH 161/282] [maven-release-plugin][skip ci] prepare release 7.16 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 00ea421da6..878882fa5c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.16-SNAPSHOT + 7.16 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.16 From ab643fdf31d937489e8866c25355aede6779adc8 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 13 Jun 2019 15:37:38 +0000 Subject: [PATCH 162/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 878882fa5c..b77f4757f1 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.16 + 7.17-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.16 + HEAD From 3441f4db518320ff2d7acce8cb8f0f2f8504838b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Fri, 14 Jun 2019 09:06:57 +0000 Subject: [PATCH 163/282] Bump dependency.spring.version from 5.1.6.RELEASE to 5.1.8.RELEASE (#68) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b77f4757f1..52fdd63c05 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ - 5.1.6.RELEASE + 5.1.8.RELEASE 7.2 11 From 1b04f88c0775c941e07e27167b883b620bd85389 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 14 Jun 2019 09:18:14 +0000 Subject: [PATCH 164/282] [maven-release-plugin][skip ci] prepare release 7.17 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 52fdd63c05..a4c17c6a14 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.17-SNAPSHOT + 7.17 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.17 From 04f29a56a41557983ace6ced666cc422ed12f951 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 14 Jun 2019 09:18:19 +0000 Subject: [PATCH 165/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a4c17c6a14..84ff78ed90 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.17 + 7.18-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.17 + HEAD From 0255f15cd5cf33956be95319a0b02d465f44fb72 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Thu, 20 Jun 2019 18:57:07 +0100 Subject: [PATCH 166/282] Add email address of release bot [skip ci] --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 46442d410c..6746592975 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,5 +40,7 @@ jobs: script: # Use full history for release - git checkout -B "${TRAVIS_BRANCH}" + # Add email to link commits to user + - git config user.email "${GIT_EMAIL}" # Skip building of release commits - - mvn --batch-mode -q -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform \ No newline at end of file + - mvn --batch-mode -q -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform From 5968f8c0ce0649f89649941a5850619b158224e3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2019 08:31:16 +0000 Subject: [PATCH 167/282] Bump joda-time from 2.10.2 to 2.10.3 (#69) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 84ff78ed90..0e55805bb3 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ joda-time joda-time - 2.10.2 + 2.10.3 org.apache.httpcomponents From 19bc1db351189988cf87580d63e9033c3628a7ed Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 5 Jul 2019 08:34:42 +0000 Subject: [PATCH 168/282] [maven-release-plugin][skip ci] prepare release 7.18 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0e55805bb3..719f549093 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.18-SNAPSHOT + 7.18 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.18 From 95dc3c86fe311e11b4f7f3a18ca2bf0cd6596231 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 5 Jul 2019 08:34:47 +0000 Subject: [PATCH 169/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 719f549093..f0e0c7c7de 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.18 + 7.19-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.18 + HEAD From a6a179dded3e03ddff2bbd0be9a3c343b262e022 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2019 15:55:11 +0000 Subject: [PATCH 170/282] Bump spring-surf-core-configservice from 7.2 to 7.4 (#70) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f0e0c7c7de..bc52f37f09 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 5.1.8.RELEASE - 7.2 + 7.4 11 From 12c59f654bdb81c9b9458e6712e87d9238f5f9ad Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 11 Jul 2019 16:05:53 +0000 Subject: [PATCH 171/282] [maven-release-plugin][skip ci] prepare release 7.19 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bc52f37f09..b81842078a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.19-SNAPSHOT + 7.19 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.19 From 04ea5663b7e44d320462b61e61224abff27bd82e Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 11 Jul 2019 16:05:58 +0000 Subject: [PATCH 172/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b81842078a..0e2138036f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.19 + 7.20-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.19 + HEAD From e09eca923321e9ce64339d04b3dbc0b03d4a41db Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2019 16:40:11 +0100 Subject: [PATCH 173/282] Bump commons-codec from 1.12 to 1.13 (#75) [no-release] Bumps [commons-codec](https://github.com/apache/commons-codec) from 1.12 to 1.13. - [Release notes](https://github.com/apache/commons-codec/releases) - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/commons-codec-1.12...commons-codec-1.13) Signed-off-by: dependabot-preview[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0e2138036f..48b06c549f 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ commons-codec commons-codec - 1.12 + 1.13 commons-collections From af619cdbdc8f45d3ed12070abe9d45e5c927bd65 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2019 16:52:49 +0100 Subject: [PATCH 174/282] Bump spring-surf-core-configservice from 7.4 to 7.8 (#72) [no-release] Bumps spring-surf-core-configservice from 7.4 to 7.8. Signed-off-by: dependabot-preview[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 48b06c549f..5aa8c1f8f7 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 5.1.8.RELEASE - 7.4 + 7.8 11 From 3a294b52939fbdddeae84420da7a25fb943daf9d Mon Sep 17 00:00:00 2001 From: anechifor Date: Tue, 6 Aug 2019 14:06:19 +0300 Subject: [PATCH 175/282] REPO-4430 : Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting') (#77) --- .../servlet/X509ServletFilterBase.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java b/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java index 11ddffdcdc..d30f688cf6 100644 --- a/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java +++ b/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.cert.X509Certificate; import java.util.Set; +import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -53,6 +54,11 @@ public abstract class X509ServletFilterBase implements Filter protected boolean enforce; private String httpsPort; private String certContains; + + /** + * The regular expression that will match the CR/LF. + */ + private static final Pattern PATTERN_CRLF = Pattern.compile("(\\r|\\n)"); private static Log logger = LogFactory.getLog(X509ServletFilterBase.class); public void init(FilterConfig config) throws ServletException @@ -169,7 +175,10 @@ public abstract class X509ServletFilterBase implements Filter { logger.debug("Redirecting to:"+redirectUrl); } - httpResponse.sendRedirect(redirectUrl); + + // MNT-18461: Prevent redirects containing CRLF + redirectUrl = sanitize(redirectUrl); + httpResponse.sendRedirect(redirectUrl); return; } } @@ -266,6 +275,17 @@ public abstract class X509ServletFilterBase implements Filter return false; } } + + + private String sanitize(String redirectUrl) + { + if (redirectUrl != null) + { + return PATTERN_CRLF.matcher(redirectUrl).replaceAll(""); + } + + return null; + } public void destroy() { From b3f810f3dcbd300a73367e4a91fdd21f760144e6 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 6 Aug 2019 11:10:11 +0000 Subject: [PATCH 176/282] [maven-release-plugin][skip ci] prepare release 7.20 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5aa8c1f8f7..fb4965d654 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.20-SNAPSHOT + 7.20 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.20 From 693a04fcf571c10a0f456fb144a0e5c55e8107a4 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 6 Aug 2019 11:10:17 +0000 Subject: [PATCH 177/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fb4965d654..78549e00e4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.20 + 7.21-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.20 + HEAD From af45041a963f71f426d0191133857d18f9f4c568 Mon Sep 17 00:00:00 2001 From: Alex Mukha Date: Tue, 13 Aug 2019 21:20:44 +0100 Subject: [PATCH 178/282] Remove jaxb-xjc and unused class (#80) --- pom.xml | 5 - .../org/alfresco/util/xml/SchemaHelper.java | 114 ------------------ 2 files changed, 119 deletions(-) delete mode 100644 src/main/java/org/alfresco/util/xml/SchemaHelper.java diff --git a/pom.xml b/pom.xml index 78549e00e4..96a6292e4b 100644 --- a/pom.xml +++ b/pom.xml @@ -132,11 +132,6 @@ jaxb-api 2.3.1 - - com.sun.xml.bind - jaxb-xjc - 2.3.2 - com.sun.xml.bind jaxb-impl diff --git a/src/main/java/org/alfresco/util/xml/SchemaHelper.java b/src/main/java/org/alfresco/util/xml/SchemaHelper.java deleted file mode 100644 index 65ae33fd31..0000000000 --- a/src/main/java/org/alfresco/util/xml/SchemaHelper.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.util.xml; - -import java.io.File; -import java.net.URL; - -import org.springframework.util.ResourceUtils; -import org.xml.sax.InputSource; -import org.xml.sax.SAXParseException; - -import com.sun.codemodel.JCodeModel; -import com.sun.tools.xjc.api.ErrorListener; -import com.sun.tools.xjc.api.S2JJAXBModel; -import com.sun.tools.xjc.api.SchemaCompiler; -import com.sun.tools.xjc.api.XJC; - -/** - * Helper to generate code from XSD files. - * - * @author Derek Hulley - * @since 3.2 - */ -public class SchemaHelper -{ - public static void main(String ... args) - { - if (args.length < 2 || !args[0].startsWith("--compile-xsd=") && !args[1].startsWith("--output-dir=")) - { - System.out.println("Usage: SchemaHelper --compile-xsd= --output-dir="); - System.exit(1); - } - final String urlStr = args[0].substring(14); - if (urlStr.length() == 0) - { - System.out.println("Usage: SchemaHelper --compile-xsd= --output-dir="); - System.exit(1); - } - final String dirStr = args[1].substring(13); - if (dirStr.length() == 0) - { - System.out.println("Usage: SchemaHelper --compile-xsd= --output-dir="); - System.exit(1); - } - try - { - URL url = ResourceUtils.getURL(urlStr); - File dir = new File(dirStr); - if (!dir.exists() || !dir.isDirectory()) - { - System.out.println("Output directory not found: " + dirStr); - System.exit(1); - } - - ErrorListener errorListener = new ErrorListener() - { - public void warning(SAXParseException e) - { - System.out.println("WARNING: " + e.getMessage()); - } - public void info(SAXParseException e) - { - System.out.println("INFO: " + e.getMessage()); - } - public void fatalError(SAXParseException e) - { - handleException(urlStr, e); - } - public void error(SAXParseException e) - { - handleException(urlStr, e); - } - }; - - SchemaCompiler compiler = XJC.createSchemaCompiler(); - compiler.setErrorListener(errorListener); - compiler.parseSchema(new InputSource(url.toExternalForm())); - S2JJAXBModel model = compiler.bind(); - if (model == null) - { - System.out.println("Failed to produce binding model for URL " + urlStr); - System.exit(1); - } - JCodeModel codeModel = model.generateCode(null, errorListener); - codeModel.build(dir); - } - catch (Throwable e) - { - handleException(urlStr, e); - System.exit(1); - } - } - private static void handleException(String urlStr, Throwable e) - { - System.out.println("Error processing XSD " + urlStr); - e.printStackTrace(); - } -} From 9a057636c4319626232e37df81ded725c043f4c9 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 13 Aug 2019 20:23:49 +0000 Subject: [PATCH 179/282] [maven-release-plugin][skip ci] prepare release 7.21 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 96a6292e4b..b8e9b31499 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.21-SNAPSHOT + 7.21 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.21 From f16e5fe616a22586d8d913c29a42c262912f7f7b Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 13 Aug 2019 20:23:55 +0000 Subject: [PATCH 180/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b8e9b31499..ff7771dcb7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.21 + 7.22-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.21 + HEAD From 765d7fe338633a2afc31483b120ec584c21aa13f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 22:46:50 +0100 Subject: [PATCH 181/282] Bump joda-time from 2.10.3 to 2.10.4 (#83) [no-release] Bumps [joda-time](https://github.com/JodaOrg/joda-time) from 2.10.3 to 2.10.4. - [Release notes](https://github.com/JodaOrg/joda-time/releases) - [Changelog](https://github.com/JodaOrg/joda-time/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/JodaOrg/joda-time/compare/v2.10.3...v2.10.4) Signed-off-by: dependabot-preview[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff7771dcb7..145d434994 100644 --- a/pom.xml +++ b/pom.xml @@ -155,7 +155,7 @@ joda-time joda-time - 2.10.3 + 2.10.4 org.apache.httpcomponents From 07e517d2d092617526b9b33255d1a3bf68174022 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 22:47:10 +0100 Subject: [PATCH 182/282] Bump spring-surf-core-configservice from 7.8 to 7.9 (#82) [no-release] Bumps spring-surf-core-configservice from 7.8 to 7.9. Signed-off-by: dependabot-preview[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 145d434994..d19bcb9564 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 5.1.8.RELEASE - 7.8 + 7.9 11 From 469e63446670ba90d0c23f307dc3cb31646280ae Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 22:47:31 +0100 Subject: [PATCH 183/282] Bump httpclient from 4.5.9 to 4.5.10 (#81) [no-release] Bumps httpclient from 4.5.9 to 4.5.10. Signed-off-by: dependabot-preview[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d19bcb9564..b5c9384c98 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ org.apache.httpcomponents httpclient - 4.5.9 + 4.5.10 From 83968094fe88d385effbaa209b3c853c68b7c5b6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 21:47:59 +0000 Subject: [PATCH 184/282] Bump slf4j-log4j12 from 1.7.26 to 1.7.28 (#79) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5c9384c98..e25fc1522a 100644 --- a/pom.xml +++ b/pom.xml @@ -175,7 +175,7 @@ org.slf4j slf4j-log4j12 - 1.7.26 + 1.7.28 test From 0623103705f4f027a2607233fbc07b0763edca2a Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 8 Oct 2019 21:55:26 +0000 Subject: [PATCH 185/282] [maven-release-plugin][skip ci] prepare release 7.22 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e25fc1522a..7d4e4169aa 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.22-SNAPSHOT + 7.22 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.22 From bc7f2c37753b90a2d4836bde7c1afd3a6e32dc3d Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 8 Oct 2019 21:55:31 +0000 Subject: [PATCH 186/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7d4e4169aa..d257cf3511 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.22 + 7.23-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.22 + HEAD From fe28cd0ff3d86a7ee0f1b8d0225b8cb50d6c2e31 Mon Sep 17 00:00:00 2001 From: Abdul Mohammed Date: Wed, 27 Nov 2019 13:58:22 +0000 Subject: [PATCH 187/282] Exclude spring-jcl sub module that is being brought in by spring-core to avoid classpath confict (#93) --- pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index d257cf3511..e4fc03969e 100644 --- a/pom.xml +++ b/pom.xml @@ -98,6 +98,14 @@ org.springframework spring-orm ${dependency.spring.version} + + + + + org.springframework + spring-jcl + + org.springframework From fe462533fb33531ff4ffa47f786f474a7075ecc2 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 27 Nov 2019 14:05:53 +0000 Subject: [PATCH 188/282] [maven-release-plugin][skip ci] prepare release 7.23 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e4fc03969e..25025b18ba 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.23-SNAPSHOT + 7.23 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.23 From 60e4154fc459ee94059da0f2723fc203af7489a9 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 27 Nov 2019 14:05:59 +0000 Subject: [PATCH 189/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 25025b18ba..40dcb76461 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.23 + 7.24-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.23 + HEAD From 2dd0694847d6fdd58904a0b364a527881713ea7b Mon Sep 17 00:00:00 2001 From: Cristian Turlica Date: Tue, 3 Dec 2019 09:47:18 +0200 Subject: [PATCH 190/282] REPO-4751: Upgrade to Spring 5.2 - The addition of support for reactive transactions in TransactionInterceptor in Spring 5.2 changed the return type of some methods in TransactionAspectSupport --- pom.xml | 2 +- .../util/transaction/SpringAwareUserTransaction.java | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 40dcb76461..4aa35cfa41 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ - 5.1.8.RELEASE + 5.2.1.RELEASE 7.9 11 diff --git a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java index 6497901ff5..2e8d6c9c26 100644 --- a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java +++ b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java @@ -36,6 +36,7 @@ import org.springframework.transaction.CannotCreateTransactionException; import org.springframework.transaction.NoTransactionException; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.transaction.interceptor.TransactionAttribute; @@ -410,8 +411,15 @@ public class SpringAwareUserTransaction // begin a transaction try { + TransactionManager tm = getTransactionManager(); + + if (tm != null && !(tm instanceof PlatformTransactionManager)) + { + throw new IllegalStateException("Specified transaction manager is not a PlatformTransactionManager: " + tm); + } + internalTxnInfo = createTransactionIfNecessary( - getTransactionManager(), getTransactionAttribute(null, null), getName()); + (PlatformTransactionManager) tm, getTransactionAttribute(null, null), getName()); } catch (CannotCreateTransactionException e) { From a377905221f3492f432f81c79bc8962bedcf615d Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 3 Dec 2019 07:51:11 +0000 Subject: [PATCH 191/282] [maven-release-plugin][skip ci] prepare release 7.24 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4aa35cfa41..953cf163a2 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.24-SNAPSHOT + 7.24 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.24 From 6e224af8a2b25cd635cd894f3f6716e38486e091 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 3 Dec 2019 07:51:16 +0000 Subject: [PATCH 192/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 953cf163a2..f3ec98c213 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.24 + 7.25-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.24 + HEAD From 8a7891a9747668c1771dc657c347d6c7cab1bfce Mon Sep 17 00:00:00 2001 From: cturlica Date: Tue, 3 Dec 2019 16:57:05 +0200 Subject: [PATCH 193/282] Prepare for next major release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 40dcb76461..70efc6664e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.24-SNAPSHOT + 7.26-SNAPSHOT Alfresco Core Alfresco core libraries and utils From ad33610d4be61ea81ec267b41febab69f2ba8088 Mon Sep 17 00:00:00 2001 From: cturlica Date: Tue, 3 Dec 2019 17:02:12 +0200 Subject: [PATCH 194/282] Changed version to 8.0 and update surf version to 8.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f3ec98c213..a4c270a089 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.25-SNAPSHOT + 8.0-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -27,7 +27,7 @@ 5.2.1.RELEASE - 7.9 + 8.0 11 From 0e6843d1072dbca7d306fb1b75bfe158cf14c60a Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 3 Dec 2019 15:03:03 +0000 Subject: [PATCH 195/282] [maven-release-plugin][skip ci] prepare release 7.26 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 70efc6664e..fbc46dc885 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.26-SNAPSHOT + 7.26 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.26 From 2ab9b5086a6b2b01bb93c9bdaf17edcf1001a7c1 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 3 Dec 2019 15:03:09 +0000 Subject: [PATCH 196/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fbc46dc885..b0643c8b6b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.26 + 7.27-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.26 + HEAD From df7a693005fbce8510ced22f6a7feb8597124cfd Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 3 Dec 2019 15:06:10 +0000 Subject: [PATCH 197/282] [maven-release-plugin][skip ci] prepare release 8.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a4c270a089..757a635bd2 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.0-SNAPSHOT + 8.0 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.0 From 50f0333f041a673281368ffd1289529fa8efb013 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 3 Dec 2019 15:06:16 +0000 Subject: [PATCH 198/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 757a635bd2..9bc57396ec 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.0 + 8.1-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.0 + HEAD From c53f69fea1ab7758fbdf7e931b078ae81a0f5113 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2019 09:38:51 +0000 Subject: [PATCH 199/282] Bump dependency.spring.version from 5.2.1.RELEASE to 5.2.2.RELEASE (#97) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9bc57396ec..2ab26f3d48 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ - 5.2.1.RELEASE + 5.2.2.RELEASE 8.0 11 From 7f2b26d730ee96ddfc5f657a0ca47dfc6b818574 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 4 Dec 2019 09:42:37 +0000 Subject: [PATCH 200/282] [maven-release-plugin][skip ci] prepare release 8.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2ab26f3d48..6caf60ccee 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.1-SNAPSHOT + 8.1 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.1 From 7e38c3bbc6cd2c292d159b6a1bd982fd4845c41e Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 4 Dec 2019 09:42:43 +0000 Subject: [PATCH 201/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6caf60ccee..76b3cf12b6 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.1 + 8.2-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.1 + HEAD From f9051c5b9aeb99b61147efe2650b991e8b9a7193 Mon Sep 17 00:00:00 2001 From: cturlica Date: Wed, 4 Dec 2019 11:46:57 +0200 Subject: [PATCH 202/282] REPO-4751: Upgrade to Spring 5.2 - update webscripts (spring 5.2.2) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76b3cf12b6..29e3b940a4 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 5.2.2.RELEASE - 8.0 + 8.1 11 From afb6235b666b2f824d4f14751735cfb6e417e0ad Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 4 Dec 2019 09:50:23 +0000 Subject: [PATCH 203/282] [maven-release-plugin][skip ci] prepare release 8.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 29e3b940a4..cea52890f7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.2-SNAPSHOT + 8.2 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.2 From ff025e9ad2c68c355a825646d8d3b0769f8ad3b6 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 4 Dec 2019 09:50:29 +0000 Subject: [PATCH 204/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cea52890f7..35eb07fc74 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.2 + 8.3-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.2 + HEAD From 38e5284ccffed3a199cd86d08ab2d38086043bb1 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2019 08:48:09 +0000 Subject: [PATCH 205/282] Bump quartz from 2.3.1 to 2.3.2 (#88) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 35eb07fc74..f08420e5d9 100644 --- a/pom.xml +++ b/pom.xml @@ -120,7 +120,7 @@ org.quartz-scheduler quartz - 2.3.1 + 2.3.2 From bd5d162d025566c25faf3799de784a46f841bef1 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 08:51:23 +0000 Subject: [PATCH 206/282] [maven-release-plugin][skip ci] prepare release 8.3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f08420e5d9..d8047ff199 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.3-SNAPSHOT + 8.3 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.3 From 6e13e02e5edeba22aee4d2ff76c8f205cb88fbdf Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 08:51:29 +0000 Subject: [PATCH 207/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d8047ff199..88b5e333be 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.3 + 8.4-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.3 + HEAD From 5b8ce87f0d81cb2af4e1394d3dba436556b5677d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2019 08:51:49 +0000 Subject: [PATCH 208/282] Bump joda-time from 2.10.4 to 2.10.5 (#89) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 88b5e333be..08eb08b879 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ joda-time joda-time - 2.10.4 + 2.10.5 org.apache.httpcomponents From 37e2e6500525570c7cc7ef9675ee11872b3f0000 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 08:55:08 +0000 Subject: [PATCH 209/282] [maven-release-plugin][skip ci] prepare release 8.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 08eb08b879..6f624ccf0c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.4-SNAPSHOT + 8.4 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.4 From 118a9db6a3c9d7e3971cf09abdea9350dffbfd9d Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 08:55:14 +0000 Subject: [PATCH 210/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6f624ccf0c..4041509418 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.4 + 8.5-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.4 + HEAD From a2325c62f0a47d509c82a706b9a9d35bd1c54f3c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2019 08:55:35 +0000 Subject: [PATCH 211/282] Bump slf4j-log4j12 from 1.7.28 to 1.7.29 (#90) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4041509418..18d957033f 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ org.slf4j slf4j-log4j12 - 1.7.28 + 1.7.29 test From bd8ef2a0f6cfc5ed1b2cbc6c1b031afe9c48b54e Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 08:58:59 +0000 Subject: [PATCH 212/282] [maven-release-plugin][skip ci] prepare release 8.5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 18d957033f..dffad0e8ad 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.5-SNAPSHOT + 8.5 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.5 From 027dcb8c83bf279712526d3da356959119dba8e9 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 08:59:05 +0000 Subject: [PATCH 213/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index dffad0e8ad..5e935cc2a3 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.5 + 8.6-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.5 + HEAD From df16735b009cc458d7e43e2c4a96e750ea13e686 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2019 08:59:48 +0000 Subject: [PATCH 214/282] Bump maven-jar-plugin from 3.1.2 to 3.2.0 (#91) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e935cc2a3..8b41abc704 100644 --- a/pom.xml +++ b/pom.xml @@ -228,7 +228,7 @@ maven-jar-plugin - 3.1.2 + 3.2.0 From 163bfb7e7640d9efc7280caf63cb69c3f541e0a6 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 09:03:18 +0000 Subject: [PATCH 215/282] [maven-release-plugin][skip ci] prepare release 8.6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8b41abc704..974e7d4bb0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.6-SNAPSHOT + 8.6 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.6 From f217f6e5f46b94b0dadb580f1588499121b1c29c Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 16 Dec 2019 09:03:24 +0000 Subject: [PATCH 216/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 974e7d4bb0..8f3bf87a55 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.6 + 8.7-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.6 + HEAD From 26fd47f1b73f42d58c18dd6d42cfcda09dd3aebe Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2019 12:31:27 +0000 Subject: [PATCH 217/282] Bump slf4j-log4j12 from 1.7.29 to 1.7.30 (#106) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8f3bf87a55..4ed3144185 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ org.slf4j slf4j-log4j12 - 1.7.29 + 1.7.30 test From 1ce04359965237e720405e7db39063077a0add6a Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 18 Dec 2019 12:35:00 +0000 Subject: [PATCH 218/282] [maven-release-plugin][skip ci] prepare release 8.7 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4ed3144185..e6c927f246 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.7-SNAPSHOT + 8.7 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.7 From 116bbf029579262f6d14752d961ca5ac9d217c15 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 18 Dec 2019 12:35:07 +0000 Subject: [PATCH 219/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e6c927f246..8ccdaa99dc 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.7 + 8.8-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.7 + HEAD From c51b3c64ba9472761f63174fadbfaf21e66926b6 Mon Sep 17 00:00:00 2001 From: NITHIN NAMBIAR Date: Thu, 19 Dec 2019 11:21:11 +0000 Subject: [PATCH 220/282] repo-4061 mockito library update (#98) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8ccdaa99dc..11895aa8da 100644 --- a/pom.xml +++ b/pom.xml @@ -200,8 +200,8 @@ org.mockito - mockito-all - 1.10.19 + mockito-core + 3.0.0 test From 125c2ac4daa6918794c61bdd1b9e5326023d9e15 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 19 Dec 2019 11:24:34 +0000 Subject: [PATCH 221/282] [maven-release-plugin][skip ci] prepare release 8.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 11895aa8da..e3e0688de2 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.8-SNAPSHOT + 8.8 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.8 From f7941d40ba5df918f0abe2799dde4118796b1a57 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 19 Dec 2019 11:24:40 +0000 Subject: [PATCH 222/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e3e0688de2..2511ec4ef5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.8 + 8.9-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.8 + HEAD From b6d8551f2ec845529369a9c0c2b34a415aa01bda Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Thu, 19 Dec 2019 16:59:50 +0000 Subject: [PATCH 223/282] Pick up new super pom with mysql and postgres versions. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2511ec4ef5..dd2b85baec 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-super-pom - 10 + 11 alfresco-core 8.9-SNAPSHOT From b22f2b9b4a4577c39cf2abf6f2d90af7d3a29dbd Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Thu, 19 Dec 2019 17:16:36 +0000 Subject: [PATCH 224/282] Revert, pick up new super pom with mysql and postgres versions. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd2b85baec..2511ec4ef5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-super-pom - 11 + 10 alfresco-core 8.9-SNAPSHOT From 892c7ecebf17ed36ebf845f589aabef14972a21f Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 19 Dec 2019 17:20:09 +0000 Subject: [PATCH 225/282] [maven-release-plugin][skip ci] prepare release 8.9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2511ec4ef5..8356359233 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.9-SNAPSHOT + 8.9 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.9 From 7bae1f171b3b2b7393abbf211906fcb7ae3386cc Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Thu, 19 Dec 2019 17:20:16 +0000 Subject: [PATCH 226/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8356359233..ea17aae76a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.9 + 8.10-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.9 + HEAD From 65dd6a7ba8a5022ccba10f4be8477416f720f458 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 08:12:39 +0000 Subject: [PATCH 227/282] Bump mockito-core from 3.0.0 to 3.2.4 (#107) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea17aae76a..e5201bf475 100644 --- a/pom.xml +++ b/pom.xml @@ -201,7 +201,7 @@ org.mockito mockito-core - 3.0.0 + 3.2.4 test From 48adb2b1e21b8eef7cd90cdad2872fa1449f03c4 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 20 Dec 2019 08:15:44 +0000 Subject: [PATCH 228/282] [maven-release-plugin][skip ci] prepare release 8.10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e5201bf475..9f8aed6c0f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.10-SNAPSHOT + 8.10 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.10 From 431f310c1f631cc0394ca8d1bc45de318b3a4e63 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 20 Dec 2019 08:15:50 +0000 Subject: [PATCH 229/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9f8aed6c0f..0381ca2238 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.10 + 8.11-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.10 + HEAD From 293a2081d096bc9524a795d6029f83475d1c8b07 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2020 08:23:46 +0000 Subject: [PATCH 230/282] Bump junit from 4.12 to 4.13 (#109) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0381ca2238..32878bc80e 100644 --- a/pom.xml +++ b/pom.xml @@ -195,7 +195,7 @@ junit junit - 4.12 + 4.13 test From cc49e908e4b7122a9d8148e2182e7c6ca53e47eb Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 3 Jan 2020 08:27:01 +0000 Subject: [PATCH 231/282] [maven-release-plugin][skip ci] prepare release 8.11 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 32878bc80e..d553d6cf2a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.11-SNAPSHOT + 8.11 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.11 From 59ec7e05a735974736a428c419dea0b07faec823 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 3 Jan 2020 08:27:07 +0000 Subject: [PATCH 232/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d553d6cf2a..1068720e17 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.11 + 8.12-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.11 + HEAD From a0751ea020e9cf1e83bba936a40c11480a093496 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2020 12:03:04 +0000 Subject: [PATCH 233/282] Bump commons-codec from 1.13 to 1.14 (#111) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1068720e17..9e9e5bf93e 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ commons-codec commons-codec - 1.13 + 1.14 commons-collections From 137cdd49afd205ca583d237018c7e1f6e6f20a03 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 6 Jan 2020 12:06:17 +0000 Subject: [PATCH 234/282] [maven-release-plugin][skip ci] prepare release 8.12 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9e9e5bf93e..e4305b3c0d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.12-SNAPSHOT + 8.12 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.12 From b50955765c3658845b7d770c4b4cf5024f6fd33b Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 6 Jan 2020 12:06:23 +0000 Subject: [PATCH 235/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e4305b3c0d..51eb6c8031 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.12 + 8.13-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.12 + HEAD From 30412802467801727f90302d6fb7a2942d72efc8 Mon Sep 17 00:00:00 2001 From: antoniojfelix Date: Wed, 8 Jan 2020 13:03:17 +0000 Subject: [PATCH 236/282] REPO-4877 - Remove library commons-collections:commons-collections from alfresco-core project (#99) --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index 51eb6c8031..0f21408837 100644 --- a/pom.xml +++ b/pom.xml @@ -37,11 +37,6 @@ commons-codec 1.14 - - commons-collections - commons-collections - 3.2.2 - commons-httpclient commons-httpclient From ef0a7d8decbe7e6d7a1ccc85c34d4df383dc333c Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:08:07 +0000 Subject: [PATCH 237/282] [maven-release-plugin][skip ci] prepare release 8.13 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0f21408837..28b74b7ac7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.13-SNAPSHOT + 8.13 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.13 From 985b04d6121d3edb6a1edf822dc7b8e55de6afed Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:08:12 +0000 Subject: [PATCH 238/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 28b74b7ac7..3fe41fe248 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.13 + 8.14-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.13 + HEAD From faa41688d6b0fbdfb465c4a6b215ff043eac94d3 Mon Sep 17 00:00:00 2001 From: antoniojfelix Date: Wed, 8 Jan 2020 13:12:08 +0000 Subject: [PATCH 239/282] REPO-4889 - Remove library org.springframework:spring-test from alfresco-core (#105) --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 3fe41fe248..0fc4fb50c2 100644 --- a/pom.xml +++ b/pom.xml @@ -181,12 +181,6 @@ 1.7.30 test - - org.springframework - spring-test - ${dependency.spring.version} - test - junit junit From c9adbfa0f413d596e942129d6afc7b77f4e16edb Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:23:54 +0000 Subject: [PATCH 240/282] [maven-release-plugin][skip ci] prepare release 8.14 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0fc4fb50c2..2219f0832b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.14-SNAPSHOT + 8.14 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.14 From 35624788295b65c6085930aac6296155254bc895 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:24:00 +0000 Subject: [PATCH 241/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2219f0832b..bc81ec85a8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.14 + 8.15-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.14 + HEAD From 9b072e85f2194d16b35cd6b1e9aa92197835ac84 Mon Sep 17 00:00:00 2001 From: antoniojfelix Date: Wed, 8 Jan 2020 13:25:42 +0000 Subject: [PATCH 242/282] REPO-4884 - Remove library org.apache.httpcomponents:httpclient from alfresco-core (#100) --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index bc81ec85a8..3b72b3cfe1 100644 --- a/pom.xml +++ b/pom.xml @@ -160,11 +160,6 @@ joda-time 2.10.5 - - org.apache.httpcomponents - httpclient - 4.5.10 - From 180a62f08d35324f25418a18385b5b08771d3c3b Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:39:40 +0000 Subject: [PATCH 243/282] [maven-release-plugin][skip ci] prepare release 8.15 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3b72b3cfe1..b78e46739e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.15-SNAPSHOT + 8.15 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.15 From 8e11ae64bb878b45728a3664a58b2f165dac1fbe Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:39:46 +0000 Subject: [PATCH 244/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b78e46739e..cf45abe689 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.15 + 8.16-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.15 + HEAD From 9556749473d1286b5457a1c2f87fe888ea968e01 Mon Sep 17 00:00:00 2001 From: antoniojfelix Date: Wed, 8 Jan 2020 13:41:26 +0000 Subject: [PATCH 245/282] REPO-4887 - Remove library org.springframework:spring-context-support from alfresco-core (#103) --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index cf45abe689..245fec2c19 100644 --- a/pom.xml +++ b/pom.xml @@ -107,11 +107,6 @@ spring-context ${dependency.spring.version} - - org.springframework - spring-context-support - ${dependency.spring.version} - org.quartz-scheduler quartz From e64cac7e6ccd544d13d4872c417e42a66974731f Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:45:13 +0000 Subject: [PATCH 246/282] [maven-release-plugin][skip ci] prepare release 8.16 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 245fec2c19..6d3fa65398 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.16-SNAPSHOT + 8.16 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.16 From 1b9b17f61f73d50c262d5c5f21b0a0dffb8c0dff Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:45:18 +0000 Subject: [PATCH 247/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6d3fa65398..de3acdd875 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.16 + 8.17-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.16 + HEAD From 7faeb5ab0a3aa8d0dcae49ad93ef9dd0126596be Mon Sep 17 00:00:00 2001 From: antoniojfelix Date: Wed, 8 Jan 2020 13:50:18 +0000 Subject: [PATCH 248/282] REPO-4886 - Remove library org.mybatis:mybatis from alfresco-core (#102) --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index de3acdd875..9dd17718ac 100644 --- a/pom.xml +++ b/pom.xml @@ -63,11 +63,6 @@ 2.0.0 asl - - org.mybatis - mybatis - 3.3.0 - org.mybatis mybatis-spring From 5a16c8fb2434c023e459aab9fec350e298bc2e98 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:53:28 +0000 Subject: [PATCH 249/282] [maven-release-plugin][skip ci] prepare release 8.17 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9dd17718ac..e523029813 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.17-SNAPSHOT + 8.17 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.17 From 19067f841a41edd29fd070a75a0a5c0895587dd4 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 13:53:34 +0000 Subject: [PATCH 250/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e523029813..1ce1a20234 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.17 + 8.18-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.17 + HEAD From 6997fe5d14ed8e048cc4262e2f702058595d614d Mon Sep 17 00:00:00 2001 From: antoniojfelix Date: Wed, 8 Jan 2020 15:57:16 +0000 Subject: [PATCH 251/282] REPO-4885 - Remove library org.mybatis:mybatis-spring from alfresco-core (#101) --- pom.xml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pom.xml b/pom.xml index 1ce1a20234..dc746682f9 100644 --- a/pom.xml +++ b/pom.xml @@ -63,17 +63,6 @@ 2.0.0 asl - - org.mybatis - mybatis-spring - 1.2.5 - - - org.mybatis - mybatis - - - log4j log4j From 316be53b00ce66b467606eb5c7be592a4c80902d Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 16:00:25 +0000 Subject: [PATCH 252/282] [maven-release-plugin][skip ci] prepare release 8.18 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index dc746682f9..35bd7254b4 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.18-SNAPSHOT + 8.18 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.18 From 114ec490f1f188954368fa0fb3d30b027d2274e6 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 8 Jan 2020 16:00:30 +0000 Subject: [PATCH 253/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 35bd7254b4..89887e87e0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.18 + 8.19-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.18 + HEAD From 18b15879fa7147364a2051326eeb8bab66898ae8 Mon Sep 17 00:00:00 2001 From: Eliza Stan <54021458+elizastan@users.noreply.github.com> Date: Mon, 13 Jan 2020 16:28:38 +0200 Subject: [PATCH 254/282] REPO-4760 - [Veracode] Add Agent-Based Scan to the Travis build (#113) * REPO-4760 - [Veracode] Add Agent-Based Scan to the Travis build --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6746592975..a642a3eefa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,12 @@ jobs: - curl -LJO https://github.com/whitesource/unified-agent-distribution/raw/master/standAlone/wss-unified-agent.jar # Run WhiteSource Unified Agent - java -jar wss-unified-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .wss-unified-agent.config + - name: "Source Clear Scan" + # only on SP branches or master and if it is not a PR + if: fork = false AND (branch = master OR branch =~ /support\/SP\/.*/) AND type != pull_request + script: skip + addons: + srcclr: true - stage: release name: "Push to Nexus" if: fork = false AND (branch = master OR branch =~ /support\/.*/) AND type != pull_request AND commit_message !~ /\[no-release\]/ From 05b29e8ac4c1808d4a06cc551fc06a377bedb9ca Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 13 Jan 2020 14:33:33 +0000 Subject: [PATCH 255/282] [maven-release-plugin][skip ci] prepare release 8.19 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 89887e87e0..ab44cf3e0a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.19-SNAPSHOT + 8.19 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.19 From 8898161890dc2e7cc60decec7d214ed810541a0a Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 13 Jan 2020 14:33:39 +0000 Subject: [PATCH 256/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ab44cf3e0a..05afffe731 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.19 + 8.20-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.19 + HEAD From b09bf2f2387e067c92a1630a9c3fd27521eae404 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2020 07:54:37 +0000 Subject: [PATCH 257/282] Bump dependency.spring.version from 5.2.2.RELEASE to 5.2.3.RELEASE (#114) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 05afffe731..ffe72bd09a 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ - 5.2.2.RELEASE + 5.2.3.RELEASE 8.1 11 From 056cc0078775db827c8add2837bff3d461b6706a Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 17 Jan 2020 09:52:16 +0000 Subject: [PATCH 258/282] [maven-release-plugin][skip ci] prepare release 8.20 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ffe72bd09a..d6e4532852 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.20-SNAPSHOT + 8.20 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.20 From cb0050d8a5622dc1251f86df84dfe1a8784222f8 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Fri, 17 Jan 2020 09:52:21 +0000 Subject: [PATCH 259/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d6e4532852..69b4ba0a50 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.20 + 8.21-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.20 + HEAD From 2c93b2d72f7e00f0b37d5f1cf5b7d2fa08577222 Mon Sep 17 00:00:00 2001 From: Tom Page Date: Mon, 20 Jan 2020 12:39:13 +0000 Subject: [PATCH 260/282] Feature/search 1785 exception in explicit sharding policy (#115) * SEARCH-1785 Add test cases and fix helper method so these fail. * SEARCH-1785 Throw exception when explicit sharding policy fails to create enough replicas. * SEARCH-1785 Update exception message to provide instructions to user. --- .../util/shard/ExplicitShardingPolicy.java | 9 +++++++++ .../shard/ExplicitShardingPolicyTest.java | 20 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java b/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java index 979d25471b..f5c421fef2 100644 --- a/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java +++ b/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java @@ -21,6 +21,8 @@ package org.alfresco.util.shard; import java.util.LinkedList; import java.util.List; +import org.alfresco.error.AlfrescoRuntimeException; + /** * Common ACL based index sharding behaviour for SOLR and the repository * @@ -67,6 +69,13 @@ public class ExplicitShardingPolicy { if (test % numNodes == nodeInstance - 1) { + // This algorithm fails for some sets of parameters. (See SEARCH-1785) + if (shardIds.contains(shard % numShards)) + { + throw new AlfrescoRuntimeException("Sharding configuration not supported - unable to create shard list for node " + nodeInstance + + " (shards:" + numShards + ", replication:" + replicationFactor + ", nodes:" + numNodes + ")." + + " Please set up the shards manually or use a different sharding configuration."); + } shardIds.add(shard % numShards); } test++; diff --git a/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java b/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java index 937c647225..ce188fb229 100644 --- a/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java +++ b/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java @@ -20,8 +20,11 @@ package org.alfresco.util.shard; import static org.junit.Assert.*; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import org.alfresco.error.AlfrescoRuntimeException; import org.junit.Test; /** @@ -116,6 +119,20 @@ public class ExplicitShardingPolicyTest { buildAndTest(10, 2, 4); } + + /** ExplicitShardingPolicy algorithm fails for 2 shards, 3 replicas, 3 nodes. (See SEARCH-1785) */ + @Test(expected = AlfrescoRuntimeException.class) + public void search1785_233() + { + buildAndTest(2, 3, 3); + } + + /** ExplicitShardingPolicy algorithm fails for 4 shards, 3 replicas, 6 nodes. (See SEARCH-1785) */ + @Test (expected = AlfrescoRuntimeException.class) + public void search1785_436() + { + buildAndTest(4, 3, 6); + } @Test public void check_10_2() @@ -163,7 +180,8 @@ public class ExplicitShardingPolicyTest int[] found = new int[numShards]; for (int i = 0; i < numNodes; i++) { - List shardIds = policy.getShardIdsForNode(i + 1); + // Convert to a set to remove any duplicates. + Set shardIds = new HashSet<>(policy.getShardIdsForNode(i + 1)); assertEquals(numShards * replicationFactor / numNodes, shardIds.size()); for (Integer shardId : shardIds) { From 4500dc2a19c3c33650ce9c9a034936d44bb9f777 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 20 Jan 2020 12:44:26 +0000 Subject: [PATCH 261/282] [maven-release-plugin][skip ci] prepare release 8.21 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 69b4ba0a50..f4befd0c90 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.21-SNAPSHOT + 8.21 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 8.21 From 3199b7677620cdb31c76915c247919e8f70075d4 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 20 Jan 2020 12:44:32 +0000 Subject: [PATCH 262/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f4befd0c90..a53355f715 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 8.21 + 8.22-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 8.21 + HEAD From cbc502feea14906a5da27e7a12100c3b508f283a Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 26 Feb 2020 13:10:37 +0000 Subject: [PATCH 263/282] [maven-release-plugin][skip ci] prepare release 7.27 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cd3c9c01ba..e64fe81114 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.27-SNAPSHOT + 7.27 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.27 From 8337ddc903bb08201222ab015bd1355294abe1e1 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 26 Feb 2020 13:10:43 +0000 Subject: [PATCH 264/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e64fe81114..76258178d7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.27 + 7.28-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.27 + HEAD From 0209c549ed9a7ba3d409a63c664ebd70038b13d5 Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Wed, 26 Feb 2020 19:46:49 +0200 Subject: [PATCH 265/282] Update surf-webscrips version to 7.12 (released from the support/SP/7.N branch) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76258178d7..32f0621605 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 5.2.3.RELEASE - 8.1 + 7.12 11 From e02c3bf1bc453785a183c5a1c7722ad509d15ff5 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 26 Feb 2020 17:52:07 +0000 Subject: [PATCH 266/282] [maven-release-plugin][skip ci] prepare release 7.28 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 32f0621605..7aaf788e80 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.28-SNAPSHOT + 7.28 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.28 From f23aa55db95210cd2f18296b72bf97dd12c39cb7 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 26 Feb 2020 17:52:12 +0000 Subject: [PATCH 267/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7aaf788e80..670044fbe7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 10 alfresco-core - 7.28 + 7.29-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.28 + HEAD From 0c0e2652b58804ab73f96868704a88f6b458a2ad Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Tue, 7 Apr 2020 01:55:32 +0100 Subject: [PATCH 268/282] REPO-4901/REPO-4904 Pick up alfresco-super-pom:12 which includes new versions of MySql annd Postgres drivers --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 670044fbe7..fec2846ec1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.alfresco alfresco-super-pom - 10 + 12 alfresco-core 7.29-SNAPSHOT From 170bcf3a0f4ba0eb10c0f4c2e4c6549650e1e17f Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 7 Apr 2020 01:00:33 +0000 Subject: [PATCH 269/282] [maven-release-plugin][skip ci] prepare release 7.29 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fec2846ec1..9cc27a0627 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.29-SNAPSHOT + 7.29 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.29 From 73b9b623b79d3cd1b7e1822c8a327ce3a9aa77cf Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 7 Apr 2020 01:00:40 +0000 Subject: [PATCH 270/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9cc27a0627..ba268c3e75 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.29 + 7.30-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.29 + HEAD From dcfc0129f7356eec6f33659701d27da1462245fc Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Mon, 20 Apr 2020 17:31:46 +0100 Subject: [PATCH 271/282] REPO-4974 Pick up latest released version of the whitesource jar (cherry picked from commit e3b4c98a7280c0f0fad70cf1eca146bc50702ca6) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a642a3eefa..203105794c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ jobs: if: fork = false AND (branch = master OR branch =~ /support\/SP\/.*/) AND type != pull_request script: # Download the latest version of WhiteSource Unified Agent - - curl -LJO https://github.com/whitesource/unified-agent-distribution/raw/master/standAlone/wss-unified-agent.jar + - curl -LJO https://github.com/whitesource/unified-agent-distribution/releases/latest/download/wss-unified-agent.jar # Run WhiteSource Unified Agent - java -jar wss-unified-agent.jar -apiKey ${WHITESOURCE_API_KEY} -c .wss-unified-agent.config - name: "Source Clear Scan" From 476731080e17a7e4f2271b95242389514da3ea71 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 20 Apr 2020 16:38:26 +0000 Subject: [PATCH 272/282] [maven-release-plugin][skip ci] prepare release 7.30 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ba268c3e75..c1c6e0d1ed 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.30-SNAPSHOT + 7.30 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.30 From edeba4ae52bb5588fdb5e2c7812722d96203d13b Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Mon, 20 Apr 2020 16:38:33 +0000 Subject: [PATCH 273/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c1c6e0d1ed..08298ad014 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.30 + 7.31-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.30 + HEAD From ce31b0647705a1c656c4c801d021a23cedd597fd Mon Sep 17 00:00:00 2001 From: CezarLeahu <35226487+CezarLeahu@users.noreply.github.com> Date: Tue, 21 Apr 2020 12:20:42 +0300 Subject: [PATCH 274/282] MNT-19887: Non-responsive SOLR address breaks admin console pages (#126) - add setter for Socket Timeout (cherry picked from commit 5609c805d48eec09b9bfe91863d350b0d6079178) --- .../org/alfresco/httpclient/HttpClientFactory.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/org/alfresco/httpclient/HttpClientFactory.java b/src/main/java/org/alfresco/httpclient/HttpClientFactory.java index 31356cb7aa..45a70358e4 100644 --- a/src/main/java/org/alfresco/httpclient/HttpClientFactory.java +++ b/src/main/java/org/alfresco/httpclient/HttpClientFactory.java @@ -249,6 +249,18 @@ public class HttpClientFactory this.maxHostConnections = maxHostConnections; } + /** + * Sets the default socket timeout (SO_TIMEOUT) in milliseconds which is the + * timeout for waiting for data. A timeout value of zero is interpreted as an infinite + * timeout. + * + * @param socketTimeout Timeout in milliseconds + */ + public void setSocketTimeout(Integer socketTimeout) + { + this.socketTimeout = socketTimeout; + } + /** * Attempts to connect to a server will timeout after this period (millis). * Default is zero (the timeout is not used). From 58afaefab13c393f37cf67a58d2e30f0b9cf15da Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 22 Apr 2020 18:31:50 +0000 Subject: [PATCH 275/282] [maven-release-plugin][skip ci] prepare release 7.31 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 08298ad014..8c38c47a35 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.31-SNAPSHOT + 7.31 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.31 From 778ff9d7b89ce42e68ead7c899cbbb01e28b1047 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 22 Apr 2020 18:31:57 +0000 Subject: [PATCH 276/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8c38c47a35..376b17865d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.31 + 7.32-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.31 + HEAD From 867eac0c54d9c4d8ed437b8ea46624f36d5f4155 Mon Sep 17 00:00:00 2001 From: Cristian Turlica Date: Tue, 28 Apr 2020 16:08:13 +0300 Subject: [PATCH 277/282] ACS-78: Revert Spring 5.2.1 update (#128) --- pom.xml | 4 ++-- .../util/transaction/SpringAwareUserTransaction.java | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 376b17865d..8e8317d5f5 100644 --- a/pom.xml +++ b/pom.xml @@ -26,8 +26,8 @@ - 5.2.3.RELEASE - 7.12 + 5.1.8.RELEASE + 7.13 11 diff --git a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java index 2e8d6c9c26..5199fcfed5 100644 --- a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java +++ b/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java @@ -36,7 +36,6 @@ import org.springframework.transaction.CannotCreateTransactionException; import org.springframework.transaction.NoTransactionException; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.transaction.interceptor.TransactionAttribute; @@ -411,15 +410,8 @@ public class SpringAwareUserTransaction // begin a transaction try { - TransactionManager tm = getTransactionManager(); - - if (tm != null && !(tm instanceof PlatformTransactionManager)) - { - throw new IllegalStateException("Specified transaction manager is not a PlatformTransactionManager: " + tm); - } - internalTxnInfo = createTransactionIfNecessary( - (PlatformTransactionManager) tm, getTransactionAttribute(null, null), getName()); + getTransactionManager(), getTransactionAttribute(null, null), getName()); } catch (CannotCreateTransactionException e) { From 979ece477e7ea802d96331abac83c66ec9b42cb6 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 28 Apr 2020 13:13:05 +0000 Subject: [PATCH 278/282] [maven-release-plugin][skip ci] prepare release 7.32 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8e8317d5f5..416f889817 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.32-SNAPSHOT + 7.32 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.32 From e2d10eaa505c46014b5d6bff68fd968ff2aedd19 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Tue, 28 Apr 2020 13:13:11 +0000 Subject: [PATCH 279/282] [maven-release-plugin][skip ci] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 416f889817..997ead7630 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.32 + 7.33-SNAPSHOT Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - 7.32 + HEAD From bffdcf2d99da8961d5eedd827a693af8fdc5c6ca Mon Sep 17 00:00:00 2001 From: Cristian Turlica Date: Wed, 6 May 2020 20:54:27 +0300 Subject: [PATCH 280/282] ACS-125: Update spring version to 5.1.15 (#131) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 997ead7630..bdaa4984b8 100644 --- a/pom.xml +++ b/pom.xml @@ -26,8 +26,8 @@ - 5.1.8.RELEASE - 7.13 + 5.1.15.RELEASE + 7.14 11 From 2d5c67fa27e80f61e667ba3e57c34609f4073a93 Mon Sep 17 00:00:00 2001 From: Travis CI User Date: Wed, 6 May 2020 17:59:35 +0000 Subject: [PATCH 281/282] [maven-release-plugin][skip ci] prepare release 7.33 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bdaa4984b8..00a51e925d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 12 alfresco-core - 7.33-SNAPSHOT + 7.33 Alfresco Core Alfresco core libraries and utils @@ -15,7 +15,7 @@ scm:git:https://github.com/Alfresco/alfresco-core.git scm:git:https://github.com/Alfresco/alfresco-core.git https://github.com/Alfresco/alfresco-core - HEAD + 7.33 From 0d61ae6002b983266bea9f93123615dbb734162a Mon Sep 17 00:00:00 2001 From: Chris Shields Date: Tue, 21 Jul 2020 10:43:20 +0100 Subject: [PATCH 282/282] Moved core support/HF/7.33.N into its own directory --- .gitattributes => core/.gitattributes | 0 .gitbugtraq => core/.gitbugtraq | 0 .gitignore => core/.gitignore | 0 .travis.settings.xml => core/.travis.settings.xml | 0 .travis.yml => core/.travis.yml | 0 .whitesource => core/.whitesource | 0 .wss-unified-agent.config => core/.wss-unified-agent.config | 0 CONTRIBUTING.md => core/CONTRIBUTING.md | 0 LICENSE.txt => core/LICENSE.txt | 0 README.md => core/README.md | 0 pom.xml => core/pom.xml | 0 .../src}/main/java/org/alfresco/api/AlfrescoPublicApi.java | 0 .../java/org/alfresco/config/AlfrescoPropertiesPersister.java | 0 .../src}/main/java/org/alfresco/config/JndiObjectFactoryBean.java | 0 .../main/java/org/alfresco/config/JndiPropertiesFactoryBean.java | 0 .../org/alfresco/config/JndiPropertyPlaceholderConfigurer.java | 0 .../java/org/alfresco/config/NonBlockingLazyInitTargetSource.java | 0 .../src}/main/java/org/alfresco/config/PathMatchingHelper.java | 0 .../java/org/alfresco/config/SystemPropertiesFactoryBean.java | 0 .../main/java/org/alfresco/config/SystemPropertiesSetterBean.java | 0 .../java/org/alfresco/encoding/AbstractCharactersetFinder.java | 0 .../main/java/org/alfresco/encoding/BomCharactersetFinder.java | 0 .../src}/main/java/org/alfresco/encoding/CharactersetFinder.java | 0 .../java/org/alfresco/encoding/GuessEncodingCharsetFinder.java | 0 .../src}/main/java/org/alfresco/encryption/AbstractEncryptor.java | 0 .../main/java/org/alfresco/encryption/AbstractKeyProvider.java | 0 .../src}/main/java/org/alfresco/encryption/AlfrescoKeyStore.java | 0 .../main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java | 0 .../src}/main/java/org/alfresco/encryption/CachedKey.java | 0 .../main/java/org/alfresco/encryption/DecryptingInputStream.java | 0 .../main/java/org/alfresco/encryption/DefaultEncryptionUtils.java | 0 .../src}/main/java/org/alfresco/encryption/DefaultEncryptor.java | 0 .../java/org/alfresco/encryption/DefaultFallbackEncryptor.java | 0 .../main/java/org/alfresco/encryption/EncryptingOutputStream.java | 0 .../main/java/org/alfresco/encryption/EncryptionKeysRegistry.java | 0 .../src}/main/java/org/alfresco/encryption/EncryptionUtils.java | 0 .../src}/main/java/org/alfresco/encryption/Encryptor.java | 0 .../src}/main/java/org/alfresco/encryption/FallbackEncryptor.java | 0 .../src}/main/java/org/alfresco/encryption/GenerateSecretKey.java | 0 .../java/org/alfresco/encryption/InvalidKeystoreException.java | 0 {src => core/src}/main/java/org/alfresco/encryption/KeyMap.java | 0 .../src}/main/java/org/alfresco/encryption/KeyProvider.java | 0 .../src}/main/java/org/alfresco/encryption/KeyResourceLoader.java | 0 .../main/java/org/alfresco/encryption/KeyStoreParameters.java | 0 .../src}/main/java/org/alfresco/encryption/KeysReport.java | 0 .../main/java/org/alfresco/encryption/KeystoreKeyProvider.java | 0 {src => core/src}/main/java/org/alfresco/encryption/MACUtils.java | 0 .../main/java/org/alfresco/encryption/MissingKeyException.java | 0 .../java/org/alfresco/encryption/SpringKeyResourceLoader.java | 0 .../org/alfresco/encryption/ssl/AuthSSLInitializationError.java | 0 .../org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java | 0 .../java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java | 0 .../main/java/org/alfresco/error/AlfrescoRuntimeException.java | 0 .../src}/main/java/org/alfresco/error/ExceptionStackUtil.java | 0 .../src}/main/java/org/alfresco/error/StackTraceUtil.java | 0 .../main/java/org/alfresco/httpclient/AbstractHttpClient.java | 0 .../main/java/org/alfresco/httpclient/AlfrescoHttpClient.java | 0 .../java/org/alfresco/httpclient/AuthenticationException.java | 0 .../src}/main/java/org/alfresco/httpclient/GetRequest.java | 0 .../src}/main/java/org/alfresco/httpclient/HeadRequest.java | 0 .../src}/main/java/org/alfresco/httpclient/HttpClientFactory.java | 0 .../main/java/org/alfresco/httpclient/HttpMethodResponse.java | 0 .../java/org/alfresco/httpclient/MD5EncryptionParameters.java | 0 .../src}/main/java/org/alfresco/httpclient/PostRequest.java | 0 {src => core/src}/main/java/org/alfresco/httpclient/Request.java | 0 {src => core/src}/main/java/org/alfresco/httpclient/Response.java | 0 .../src}/main/java/org/alfresco/httpclient/SecureHttpClient.java | 0 .../java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java | 0 {src => core/src}/main/java/org/alfresco/processor/Processor.java | 0 .../src}/main/java/org/alfresco/processor/ProcessorExtension.java | 0 .../org/alfresco/query/AbstractCachingCannedQueryFactory.java | 0 .../src}/main/java/org/alfresco/query/AbstractCannedQuery.java | 0 .../main/java/org/alfresco/query/AbstractCannedQueryFactory.java | 0 {src => core/src}/main/java/org/alfresco/query/CannedQuery.java | 0 .../src}/main/java/org/alfresco/query/CannedQueryException.java | 0 .../src}/main/java/org/alfresco/query/CannedQueryFactory.java | 0 .../src}/main/java/org/alfresco/query/CannedQueryPageDetails.java | 0 .../src}/main/java/org/alfresco/query/CannedQueryParameters.java | 0 .../src}/main/java/org/alfresco/query/CannedQueryResults.java | 0 .../src}/main/java/org/alfresco/query/CannedQuerySortDetails.java | 0 .../main/java/org/alfresco/query/EmptyCannedQueryResults.java | 0 .../src}/main/java/org/alfresco/query/EmptyPagingResults.java | 0 .../main/java/org/alfresco/query/ListBackedPagingResults.java | 0 {src => core/src}/main/java/org/alfresco/query/PageDetails.java | 0 {src => core/src}/main/java/org/alfresco/query/PagingRequest.java | 0 {src => core/src}/main/java/org/alfresco/query/PagingResults.java | 0 .../src}/main/java/org/alfresco/query/PermissionedResults.java | 0 .../src}/main/java/org/alfresco/scripts/ScriptException.java | 0 .../src}/main/java/org/alfresco/scripts/ScriptResourceHelper.java | 0 .../src}/main/java/org/alfresco/scripts/ScriptResourceLoader.java | 0 {src => core/src}/main/java/org/alfresco/util/ArgumentHelper.java | 0 {src => core/src}/main/java/org/alfresco/util/BridgeTable.java | 0 .../src}/main/java/org/alfresco/util/CachingDateFormat.java | 0 {src => core/src}/main/java/org/alfresco/util/Content.java | 0 {src => core/src}/main/java/org/alfresco/util/Convert.java | 0 {src => core/src}/main/java/org/alfresco/util/Debug.java | 0 {src => core/src}/main/java/org/alfresco/util/Deleter.java | 0 .../org/alfresco/util/DynamicallySizedThreadPoolExecutor.java | 0 {src => core/src}/main/java/org/alfresco/util/EqualsHelper.java | 0 .../src}/main/java/org/alfresco/util/ExpiringValueCache.java | 0 {src => core/src}/main/java/org/alfresco/util/FileFilterMode.java | 0 {src => core/src}/main/java/org/alfresco/util/GUID.java | 0 {src => core/src}/main/java/org/alfresco/util/IPUtils.java | 0 .../src}/main/java/org/alfresco/util/ISO8601DateFormat.java | 0 .../src}/main/java/org/alfresco/util/InputStreamContent.java | 0 {src => core/src}/main/java/org/alfresco/util/JMXUtils.java | 0 {src => core/src}/main/java/org/alfresco/util/LockHelper.java | 0 {src => core/src}/main/java/org/alfresco/util/LogAdapter.java | 0 {src => core/src}/main/java/org/alfresco/util/LogTee.java | 0 {src => core/src}/main/java/org/alfresco/util/LogUtil.java | 0 {src => core/src}/main/java/org/alfresco/util/MD5.java | 0 {src => core/src}/main/java/org/alfresco/util/MaxSizeMap.java | 0 {src => core/src}/main/java/org/alfresco/util/OneToManyBiMap.java | 0 .../src}/main/java/org/alfresco/util/OneToManyHashBiMap.java | 0 .../src}/main/java/org/alfresco/util/OneToManyHashMap.java | 0 {src => core/src}/main/java/org/alfresco/util/OneToManyMap.java | 0 {src => core/src}/main/java/org/alfresco/util/PackageMarker.java | 0 {src => core/src}/main/java/org/alfresco/util/Pair.java | 0 {src => core/src}/main/java/org/alfresco/util/ParameterCheck.java | 0 {src => core/src}/main/java/org/alfresco/util/PathMapper.java | 0 {src => core/src}/main/java/org/alfresco/util/PatternFilter.java | 0 {src => core/src}/main/java/org/alfresco/util/PropertyCheck.java | 0 .../src}/main/java/org/alfresco/util/ReadWriteLockExecuter.java | 0 .../src}/main/java/org/alfresco/util/ReflectionHelper.java | 0 .../src}/main/java/org/alfresco/util/SchedulerStarterBean.java | 0 .../src}/main/java/org/alfresco/util/SerializationUtils.java | 0 .../src}/main/java/org/alfresco/util/TempFileProvider.java | 0 .../src}/main/java/org/alfresco/util/TraceableThreadFactory.java | 0 {src => core/src}/main/java/org/alfresco/util/TriggerBeanSPI.java | 0 {src => core/src}/main/java/org/alfresco/util/Triple.java | 0 {src => core/src}/main/java/org/alfresco/util/VersionNumber.java | 0 .../src}/main/java/org/alfresco/util/VmShutdownListener.java | 0 .../src}/main/java/org/alfresco/util/bean/BooleanBean.java | 0 .../main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java | 0 .../alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java | 0 .../org/alfresco/util/cache/AbstractRefreshableCacheEvent.java | 0 .../org/alfresco/util/cache/AsynchronouslyRefreshedCache.java | 0 .../alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java | 0 .../util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java | 0 .../src}/main/java/org/alfresco/util/cache/RefreshableCache.java | 0 .../main/java/org/alfresco/util/cache/RefreshableCacheEvent.java | 0 .../java/org/alfresco/util/cache/RefreshableCacheListener.java | 0 .../org/alfresco/util/cache/RefreshableCacheRefreshEvent.java | 0 .../org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java | 0 .../main/java/org/alfresco/util/collections/CollectionUtils.java | 0 .../main/java/org/alfresco/util/collections/EntryTransformer.java | 0 .../src}/main/java/org/alfresco/util/collections/Filter.java | 0 .../src}/main/java/org/alfresco/util/collections/Function.java | 0 .../src}/main/java/org/alfresco/util/collections/JsonUtils.java | 0 .../main/java/org/alfresco/util/exec/ExecParameterTokenizer.java | 0 .../src}/main/java/org/alfresco/util/exec/RuntimeExec.java | 0 .../java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java | 0 .../main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java | 0 {src => core/src}/main/java/org/alfresco/util/log/NDC.java | 0 .../src}/main/java/org/alfresco/util/log/NDCDelegate.java | 0 .../src}/main/java/org/alfresco/util/log/log4j/Log4JNDC.java | 0 .../java/org/alfresco/util/random/NormalDistributionHelper.java | 0 .../main/java/org/alfresco/util/registry/NamedObjectRegistry.java | 0 .../main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java | 0 .../org/alfresco/util/transaction/ConnectionPoolException.java | 0 .../org/alfresco/util/transaction/SpringAwareUserTransaction.java | 0 .../java/org/alfresco/util/transaction/TransactionListener.java | 0 .../org/alfresco/util/transaction/TransactionListenerAdapter.java | 0 .../org/alfresco/util/transaction/TransactionSupportUtil.java | 0 .../org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java | 0 .../org/alfresco/web/scripts/servlet/X509ServletFilterBase.java | 0 .../java/org/alfresco/config/SystemPropertiesSetterBeanTest.java | 0 .../java/org/alfresco/encryption/EncryptingOutputStreamTest.java | 0 .../java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java | 0 .../src}/test/java/org/alfresco/query/CannedQueryTest.java | 0 {src => core/src}/test/java/org/alfresco/util/BaseTest.java | 0 .../src}/test/java/org/alfresco/util/BridgeTableTest.java | 0 .../src}/test/java/org/alfresco/util/CachingDateFormatTest.java | 0 .../org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java | 0 .../src}/test/java/org/alfresco/util/EqualsHelperTest.java | 0 {src => core/src}/test/java/org/alfresco/util/GuidTest.java | 0 .../src}/test/java/org/alfresco/util/ISO8601DateFormatTest.java | 0 {src => core/src}/test/java/org/alfresco/util/LogAdapterTest.java | 0 {src => core/src}/test/java/org/alfresco/util/LogTeeTest.java | 0 {src => core/src}/test/java/org/alfresco/util/PathMapperTest.java | 0 .../src}/test/java/org/alfresco/util/TempFileProviderTest.java | 0 .../src}/test/java/org/alfresco/util/VersionNumberTest.java | 0 .../java/org/alfresco/util/collections/CollectionUtilsTest.java | 0 .../java/org/alfresco/util/exec/ExecParameterTokenizerTest.java | 0 .../test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java | 0 .../src}/test/java/org/alfresco/util/exec/RuntimeExecTest.java | 0 .../org/alfresco/util/random/NormalDistributionHelperTest.java | 0 .../java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java | 0 .../test/java/org/alfresco/util/testing/category/DBTests.java | 0 .../test/java/org/alfresco/util/testing/category/DebugTests.java | 0 .../test/java/org/alfresco/util/testing/category/LuceneTests.java | 0 .../java/org/alfresco/util/testing/category/NeverRunsTests.java | 0 .../java/org/alfresco/util/testing/category/NonBuildTests.java | 0 .../java/org/alfresco/util/testing/category/PerformanceTests.java | 0 .../java/org/alfresco/util/testing/category/RedundantTests.java | 0 .../test/java/org/alfresco/util/testing/category/SanityTests.java | 0 .../test/java/org/alfresco/util/testing/category/SlowTests.java | 0 .../alfresco/util/transaction/SpringAwareUserTransactionTest.java | 0 {src => core/src}/test/resources/config-areas.xml | 0 {src => core/src}/test/resources/config-multi.xml | 0 {src => core/src}/test/resources/config-props.properties | 0 {src => core/src}/test/resources/config-props.xml | 0 {src => core/src}/test/resources/config-replace.xml | 0 {src => core/src}/test/resources/config.xml | 0 .../src}/test/resources/org/alfresco/i18n/testMessages.properties | 0 .../resources/org/alfresco/i18n/testMessages_fr_FR.properties | 0 .../org/alfresco/util/exec/RuntimeExecBeansTest-context.xml | 0 .../resource-loader/java.util.AbstractCollection/file.txt | 0 .../resources/resource-loader/java.util.AbstractList/file.txt | 0 .../test/resources/resource-loader/java.util.TreeSet/file.txt | 0 .../src}/test/resources/test-config-forms-basic-override.xml | 0 {src => core/src}/test/resources/test-config-forms-basic.xml | 0 {src => core/src}/test/resources/test-config-forms-negative.xml | 0 {src => core/src}/test/resources/test-config-forms.xml | 0 214 files changed, 0 insertions(+), 0 deletions(-) rename .gitattributes => core/.gitattributes (100%) rename .gitbugtraq => core/.gitbugtraq (100%) rename .gitignore => core/.gitignore (100%) rename .travis.settings.xml => core/.travis.settings.xml (100%) rename .travis.yml => core/.travis.yml (100%) rename .whitesource => core/.whitesource (100%) rename .wss-unified-agent.config => core/.wss-unified-agent.config (100%) rename CONTRIBUTING.md => core/CONTRIBUTING.md (100%) rename LICENSE.txt => core/LICENSE.txt (100%) rename README.md => core/README.md (100%) rename pom.xml => core/pom.xml (100%) rename {src => core/src}/main/java/org/alfresco/api/AlfrescoPublicApi.java (100%) rename {src => core/src}/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java (100%) rename {src => core/src}/main/java/org/alfresco/config/JndiObjectFactoryBean.java (100%) rename {src => core/src}/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java (100%) rename {src => core/src}/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java (100%) rename {src => core/src}/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java (100%) rename {src => core/src}/main/java/org/alfresco/config/PathMatchingHelper.java (100%) rename {src => core/src}/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java (100%) rename {src => core/src}/main/java/org/alfresco/config/SystemPropertiesSetterBean.java (100%) rename {src => core/src}/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java (100%) rename {src => core/src}/main/java/org/alfresco/encoding/BomCharactersetFinder.java (100%) rename {src => core/src}/main/java/org/alfresco/encoding/CharactersetFinder.java (100%) rename {src => core/src}/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/AbstractEncryptor.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/AbstractKeyProvider.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/AlfrescoKeyStore.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/CachedKey.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/DecryptingInputStream.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/DefaultEncryptor.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/EncryptingOutputStream.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/EncryptionUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/Encryptor.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/FallbackEncryptor.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/GenerateSecretKey.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/InvalidKeystoreException.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/KeyMap.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/KeyProvider.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/KeyResourceLoader.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/KeyStoreParameters.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/KeysReport.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/KeystoreKeyProvider.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/MACUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/MissingKeyException.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java (100%) rename {src => core/src}/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java (100%) rename {src => core/src}/main/java/org/alfresco/error/AlfrescoRuntimeException.java (100%) rename {src => core/src}/main/java/org/alfresco/error/ExceptionStackUtil.java (100%) rename {src => core/src}/main/java/org/alfresco/error/StackTraceUtil.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/AbstractHttpClient.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/AuthenticationException.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/GetRequest.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/HeadRequest.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/HttpClientFactory.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/HttpMethodResponse.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/PostRequest.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/Request.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/Response.java (100%) rename {src => core/src}/main/java/org/alfresco/httpclient/SecureHttpClient.java (100%) rename {src => core/src}/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java (100%) rename {src => core/src}/main/java/org/alfresco/processor/Processor.java (100%) rename {src => core/src}/main/java/org/alfresco/processor/ProcessorExtension.java (100%) rename {src => core/src}/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java (100%) rename {src => core/src}/main/java/org/alfresco/query/AbstractCannedQuery.java (100%) rename {src => core/src}/main/java/org/alfresco/query/AbstractCannedQueryFactory.java (100%) rename {src => core/src}/main/java/org/alfresco/query/CannedQuery.java (100%) rename {src => core/src}/main/java/org/alfresco/query/CannedQueryException.java (100%) rename {src => core/src}/main/java/org/alfresco/query/CannedQueryFactory.java (100%) rename {src => core/src}/main/java/org/alfresco/query/CannedQueryPageDetails.java (100%) rename {src => core/src}/main/java/org/alfresco/query/CannedQueryParameters.java (100%) rename {src => core/src}/main/java/org/alfresco/query/CannedQueryResults.java (100%) rename {src => core/src}/main/java/org/alfresco/query/CannedQuerySortDetails.java (100%) rename {src => core/src}/main/java/org/alfresco/query/EmptyCannedQueryResults.java (100%) rename {src => core/src}/main/java/org/alfresco/query/EmptyPagingResults.java (100%) rename {src => core/src}/main/java/org/alfresco/query/ListBackedPagingResults.java (100%) rename {src => core/src}/main/java/org/alfresco/query/PageDetails.java (100%) rename {src => core/src}/main/java/org/alfresco/query/PagingRequest.java (100%) rename {src => core/src}/main/java/org/alfresco/query/PagingResults.java (100%) rename {src => core/src}/main/java/org/alfresco/query/PermissionedResults.java (100%) rename {src => core/src}/main/java/org/alfresco/scripts/ScriptException.java (100%) rename {src => core/src}/main/java/org/alfresco/scripts/ScriptResourceHelper.java (100%) rename {src => core/src}/main/java/org/alfresco/scripts/ScriptResourceLoader.java (100%) rename {src => core/src}/main/java/org/alfresco/util/ArgumentHelper.java (100%) rename {src => core/src}/main/java/org/alfresco/util/BridgeTable.java (100%) rename {src => core/src}/main/java/org/alfresco/util/CachingDateFormat.java (100%) rename {src => core/src}/main/java/org/alfresco/util/Content.java (100%) rename {src => core/src}/main/java/org/alfresco/util/Convert.java (100%) rename {src => core/src}/main/java/org/alfresco/util/Debug.java (100%) rename {src => core/src}/main/java/org/alfresco/util/Deleter.java (100%) rename {src => core/src}/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java (100%) rename {src => core/src}/main/java/org/alfresco/util/EqualsHelper.java (100%) rename {src => core/src}/main/java/org/alfresco/util/ExpiringValueCache.java (100%) rename {src => core/src}/main/java/org/alfresco/util/FileFilterMode.java (100%) rename {src => core/src}/main/java/org/alfresco/util/GUID.java (100%) rename {src => core/src}/main/java/org/alfresco/util/IPUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/util/ISO8601DateFormat.java (100%) rename {src => core/src}/main/java/org/alfresco/util/InputStreamContent.java (100%) rename {src => core/src}/main/java/org/alfresco/util/JMXUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/util/LockHelper.java (100%) rename {src => core/src}/main/java/org/alfresco/util/LogAdapter.java (100%) rename {src => core/src}/main/java/org/alfresco/util/LogTee.java (100%) rename {src => core/src}/main/java/org/alfresco/util/LogUtil.java (100%) rename {src => core/src}/main/java/org/alfresco/util/MD5.java (100%) rename {src => core/src}/main/java/org/alfresco/util/MaxSizeMap.java (100%) rename {src => core/src}/main/java/org/alfresco/util/OneToManyBiMap.java (100%) rename {src => core/src}/main/java/org/alfresco/util/OneToManyHashBiMap.java (100%) rename {src => core/src}/main/java/org/alfresco/util/OneToManyHashMap.java (100%) rename {src => core/src}/main/java/org/alfresco/util/OneToManyMap.java (100%) rename {src => core/src}/main/java/org/alfresco/util/PackageMarker.java (100%) rename {src => core/src}/main/java/org/alfresco/util/Pair.java (100%) rename {src => core/src}/main/java/org/alfresco/util/ParameterCheck.java (100%) rename {src => core/src}/main/java/org/alfresco/util/PathMapper.java (100%) rename {src => core/src}/main/java/org/alfresco/util/PatternFilter.java (100%) rename {src => core/src}/main/java/org/alfresco/util/PropertyCheck.java (100%) rename {src => core/src}/main/java/org/alfresco/util/ReadWriteLockExecuter.java (100%) rename {src => core/src}/main/java/org/alfresco/util/ReflectionHelper.java (100%) rename {src => core/src}/main/java/org/alfresco/util/SchedulerStarterBean.java (100%) rename {src => core/src}/main/java/org/alfresco/util/SerializationUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/util/TempFileProvider.java (100%) rename {src => core/src}/main/java/org/alfresco/util/TraceableThreadFactory.java (100%) rename {src => core/src}/main/java/org/alfresco/util/TriggerBeanSPI.java (100%) rename {src => core/src}/main/java/org/alfresco/util/Triple.java (100%) rename {src => core/src}/main/java/org/alfresco/util/VersionNumber.java (100%) rename {src => core/src}/main/java/org/alfresco/util/VmShutdownListener.java (100%) rename {src => core/src}/main/java/org/alfresco/util/bean/BooleanBean.java (100%) rename {src => core/src}/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/RefreshableCache.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/RefreshableCacheListener.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java (100%) rename {src => core/src}/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java (100%) rename {src => core/src}/main/java/org/alfresco/util/collections/CollectionUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/util/collections/EntryTransformer.java (100%) rename {src => core/src}/main/java/org/alfresco/util/collections/Filter.java (100%) rename {src => core/src}/main/java/org/alfresco/util/collections/Function.java (100%) rename {src => core/src}/main/java/org/alfresco/util/collections/JsonUtils.java (100%) rename {src => core/src}/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java (100%) rename {src => core/src}/main/java/org/alfresco/util/exec/RuntimeExec.java (100%) rename {src => core/src}/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java (100%) rename {src => core/src}/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java (100%) rename {src => core/src}/main/java/org/alfresco/util/log/NDC.java (100%) rename {src => core/src}/main/java/org/alfresco/util/log/NDCDelegate.java (100%) rename {src => core/src}/main/java/org/alfresco/util/log/log4j/Log4JNDC.java (100%) rename {src => core/src}/main/java/org/alfresco/util/random/NormalDistributionHelper.java (100%) rename {src => core/src}/main/java/org/alfresco/util/registry/NamedObjectRegistry.java (100%) rename {src => core/src}/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java (100%) rename {src => core/src}/main/java/org/alfresco/util/transaction/ConnectionPoolException.java (100%) rename {src => core/src}/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java (100%) rename {src => core/src}/main/java/org/alfresco/util/transaction/TransactionListener.java (100%) rename {src => core/src}/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java (100%) rename {src => core/src}/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java (100%) rename {src => core/src}/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java (100%) rename {src => core/src}/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java (100%) rename {src => core/src}/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java (100%) rename {src => core/src}/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java (100%) rename {src => core/src}/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java (100%) rename {src => core/src}/test/java/org/alfresco/query/CannedQueryTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/BaseTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/BridgeTableTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/CachingDateFormatTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/EqualsHelperTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/GuidTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/ISO8601DateFormatTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/LogAdapterTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/LogTeeTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/PathMapperTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/TempFileProviderTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/VersionNumberTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/collections/CollectionUtilsTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/exec/RuntimeExecTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/DBTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/DebugTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/LuceneTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/NeverRunsTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/NonBuildTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/PerformanceTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/RedundantTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/SanityTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/testing/category/SlowTests.java (100%) rename {src => core/src}/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java (100%) rename {src => core/src}/test/resources/config-areas.xml (100%) rename {src => core/src}/test/resources/config-multi.xml (100%) rename {src => core/src}/test/resources/config-props.properties (100%) rename {src => core/src}/test/resources/config-props.xml (100%) rename {src => core/src}/test/resources/config-replace.xml (100%) rename {src => core/src}/test/resources/config.xml (100%) rename {src => core/src}/test/resources/org/alfresco/i18n/testMessages.properties (100%) rename {src => core/src}/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties (100%) rename {src => core/src}/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml (100%) rename {src => core/src}/test/resources/resource-loader/java.util.AbstractCollection/file.txt (100%) rename {src => core/src}/test/resources/resource-loader/java.util.AbstractList/file.txt (100%) rename {src => core/src}/test/resources/resource-loader/java.util.TreeSet/file.txt (100%) rename {src => core/src}/test/resources/test-config-forms-basic-override.xml (100%) rename {src => core/src}/test/resources/test-config-forms-basic.xml (100%) rename {src => core/src}/test/resources/test-config-forms-negative.xml (100%) rename {src => core/src}/test/resources/test-config-forms.xml (100%) diff --git a/.gitattributes b/core/.gitattributes similarity index 100% rename from .gitattributes rename to core/.gitattributes diff --git a/.gitbugtraq b/core/.gitbugtraq similarity index 100% rename from .gitbugtraq rename to core/.gitbugtraq diff --git a/.gitignore b/core/.gitignore similarity index 100% rename from .gitignore rename to core/.gitignore diff --git a/.travis.settings.xml b/core/.travis.settings.xml similarity index 100% rename from .travis.settings.xml rename to core/.travis.settings.xml diff --git a/.travis.yml b/core/.travis.yml similarity index 100% rename from .travis.yml rename to core/.travis.yml diff --git a/.whitesource b/core/.whitesource similarity index 100% rename from .whitesource rename to core/.whitesource diff --git a/.wss-unified-agent.config b/core/.wss-unified-agent.config similarity index 100% rename from .wss-unified-agent.config rename to core/.wss-unified-agent.config diff --git a/CONTRIBUTING.md b/core/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to core/CONTRIBUTING.md diff --git a/LICENSE.txt b/core/LICENSE.txt similarity index 100% rename from LICENSE.txt rename to core/LICENSE.txt diff --git a/README.md b/core/README.md similarity index 100% rename from README.md rename to core/README.md diff --git a/pom.xml b/core/pom.xml similarity index 100% rename from pom.xml rename to core/pom.xml diff --git a/src/main/java/org/alfresco/api/AlfrescoPublicApi.java b/core/src/main/java/org/alfresco/api/AlfrescoPublicApi.java similarity index 100% rename from src/main/java/org/alfresco/api/AlfrescoPublicApi.java rename to core/src/main/java/org/alfresco/api/AlfrescoPublicApi.java diff --git a/src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java b/core/src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java similarity index 100% rename from src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java rename to core/src/main/java/org/alfresco/config/AlfrescoPropertiesPersister.java diff --git a/src/main/java/org/alfresco/config/JndiObjectFactoryBean.java b/core/src/main/java/org/alfresco/config/JndiObjectFactoryBean.java similarity index 100% rename from src/main/java/org/alfresco/config/JndiObjectFactoryBean.java rename to core/src/main/java/org/alfresco/config/JndiObjectFactoryBean.java diff --git a/src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java b/core/src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java similarity index 100% rename from src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java rename to core/src/main/java/org/alfresco/config/JndiPropertiesFactoryBean.java diff --git a/src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java b/core/src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java similarity index 100% rename from src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java rename to core/src/main/java/org/alfresco/config/JndiPropertyPlaceholderConfigurer.java diff --git a/src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java b/core/src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java similarity index 100% rename from src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java rename to core/src/main/java/org/alfresco/config/NonBlockingLazyInitTargetSource.java diff --git a/src/main/java/org/alfresco/config/PathMatchingHelper.java b/core/src/main/java/org/alfresco/config/PathMatchingHelper.java similarity index 100% rename from src/main/java/org/alfresco/config/PathMatchingHelper.java rename to core/src/main/java/org/alfresco/config/PathMatchingHelper.java diff --git a/src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java b/core/src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java similarity index 100% rename from src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java rename to core/src/main/java/org/alfresco/config/SystemPropertiesFactoryBean.java diff --git a/src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java b/core/src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java similarity index 100% rename from src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java rename to core/src/main/java/org/alfresco/config/SystemPropertiesSetterBean.java diff --git a/src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java b/core/src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java similarity index 100% rename from src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java rename to core/src/main/java/org/alfresco/encoding/AbstractCharactersetFinder.java diff --git a/src/main/java/org/alfresco/encoding/BomCharactersetFinder.java b/core/src/main/java/org/alfresco/encoding/BomCharactersetFinder.java similarity index 100% rename from src/main/java/org/alfresco/encoding/BomCharactersetFinder.java rename to core/src/main/java/org/alfresco/encoding/BomCharactersetFinder.java diff --git a/src/main/java/org/alfresco/encoding/CharactersetFinder.java b/core/src/main/java/org/alfresco/encoding/CharactersetFinder.java similarity index 100% rename from src/main/java/org/alfresco/encoding/CharactersetFinder.java rename to core/src/main/java/org/alfresco/encoding/CharactersetFinder.java diff --git a/src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java b/core/src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java similarity index 100% rename from src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java rename to core/src/main/java/org/alfresco/encoding/GuessEncodingCharsetFinder.java diff --git a/src/main/java/org/alfresco/encryption/AbstractEncryptor.java b/core/src/main/java/org/alfresco/encryption/AbstractEncryptor.java similarity index 100% rename from src/main/java/org/alfresco/encryption/AbstractEncryptor.java rename to core/src/main/java/org/alfresco/encryption/AbstractEncryptor.java diff --git a/src/main/java/org/alfresco/encryption/AbstractKeyProvider.java b/core/src/main/java/org/alfresco/encryption/AbstractKeyProvider.java similarity index 100% rename from src/main/java/org/alfresco/encryption/AbstractKeyProvider.java rename to core/src/main/java/org/alfresco/encryption/AbstractKeyProvider.java diff --git a/src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java b/core/src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java similarity index 100% rename from src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java rename to core/src/main/java/org/alfresco/encryption/AlfrescoKeyStore.java diff --git a/src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java b/core/src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java similarity index 100% rename from src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java rename to core/src/main/java/org/alfresco/encryption/AlfrescoKeyStoreImpl.java diff --git a/src/main/java/org/alfresco/encryption/CachedKey.java b/core/src/main/java/org/alfresco/encryption/CachedKey.java similarity index 100% rename from src/main/java/org/alfresco/encryption/CachedKey.java rename to core/src/main/java/org/alfresco/encryption/CachedKey.java diff --git a/src/main/java/org/alfresco/encryption/DecryptingInputStream.java b/core/src/main/java/org/alfresco/encryption/DecryptingInputStream.java similarity index 100% rename from src/main/java/org/alfresco/encryption/DecryptingInputStream.java rename to core/src/main/java/org/alfresco/encryption/DecryptingInputStream.java diff --git a/src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java b/core/src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java similarity index 100% rename from src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java rename to core/src/main/java/org/alfresco/encryption/DefaultEncryptionUtils.java diff --git a/src/main/java/org/alfresco/encryption/DefaultEncryptor.java b/core/src/main/java/org/alfresco/encryption/DefaultEncryptor.java similarity index 100% rename from src/main/java/org/alfresco/encryption/DefaultEncryptor.java rename to core/src/main/java/org/alfresco/encryption/DefaultEncryptor.java diff --git a/src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java b/core/src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java similarity index 100% rename from src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java rename to core/src/main/java/org/alfresco/encryption/DefaultFallbackEncryptor.java diff --git a/src/main/java/org/alfresco/encryption/EncryptingOutputStream.java b/core/src/main/java/org/alfresco/encryption/EncryptingOutputStream.java similarity index 100% rename from src/main/java/org/alfresco/encryption/EncryptingOutputStream.java rename to core/src/main/java/org/alfresco/encryption/EncryptingOutputStream.java diff --git a/src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java b/core/src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java similarity index 100% rename from src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java rename to core/src/main/java/org/alfresco/encryption/EncryptionKeysRegistry.java diff --git a/src/main/java/org/alfresco/encryption/EncryptionUtils.java b/core/src/main/java/org/alfresco/encryption/EncryptionUtils.java similarity index 100% rename from src/main/java/org/alfresco/encryption/EncryptionUtils.java rename to core/src/main/java/org/alfresco/encryption/EncryptionUtils.java diff --git a/src/main/java/org/alfresco/encryption/Encryptor.java b/core/src/main/java/org/alfresco/encryption/Encryptor.java similarity index 100% rename from src/main/java/org/alfresco/encryption/Encryptor.java rename to core/src/main/java/org/alfresco/encryption/Encryptor.java diff --git a/src/main/java/org/alfresco/encryption/FallbackEncryptor.java b/core/src/main/java/org/alfresco/encryption/FallbackEncryptor.java similarity index 100% rename from src/main/java/org/alfresco/encryption/FallbackEncryptor.java rename to core/src/main/java/org/alfresco/encryption/FallbackEncryptor.java diff --git a/src/main/java/org/alfresco/encryption/GenerateSecretKey.java b/core/src/main/java/org/alfresco/encryption/GenerateSecretKey.java similarity index 100% rename from src/main/java/org/alfresco/encryption/GenerateSecretKey.java rename to core/src/main/java/org/alfresco/encryption/GenerateSecretKey.java diff --git a/src/main/java/org/alfresco/encryption/InvalidKeystoreException.java b/core/src/main/java/org/alfresco/encryption/InvalidKeystoreException.java similarity index 100% rename from src/main/java/org/alfresco/encryption/InvalidKeystoreException.java rename to core/src/main/java/org/alfresco/encryption/InvalidKeystoreException.java diff --git a/src/main/java/org/alfresco/encryption/KeyMap.java b/core/src/main/java/org/alfresco/encryption/KeyMap.java similarity index 100% rename from src/main/java/org/alfresco/encryption/KeyMap.java rename to core/src/main/java/org/alfresco/encryption/KeyMap.java diff --git a/src/main/java/org/alfresco/encryption/KeyProvider.java b/core/src/main/java/org/alfresco/encryption/KeyProvider.java similarity index 100% rename from src/main/java/org/alfresco/encryption/KeyProvider.java rename to core/src/main/java/org/alfresco/encryption/KeyProvider.java diff --git a/src/main/java/org/alfresco/encryption/KeyResourceLoader.java b/core/src/main/java/org/alfresco/encryption/KeyResourceLoader.java similarity index 100% rename from src/main/java/org/alfresco/encryption/KeyResourceLoader.java rename to core/src/main/java/org/alfresco/encryption/KeyResourceLoader.java diff --git a/src/main/java/org/alfresco/encryption/KeyStoreParameters.java b/core/src/main/java/org/alfresco/encryption/KeyStoreParameters.java similarity index 100% rename from src/main/java/org/alfresco/encryption/KeyStoreParameters.java rename to core/src/main/java/org/alfresco/encryption/KeyStoreParameters.java diff --git a/src/main/java/org/alfresco/encryption/KeysReport.java b/core/src/main/java/org/alfresco/encryption/KeysReport.java similarity index 100% rename from src/main/java/org/alfresco/encryption/KeysReport.java rename to core/src/main/java/org/alfresco/encryption/KeysReport.java diff --git a/src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java b/core/src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java similarity index 100% rename from src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java rename to core/src/main/java/org/alfresco/encryption/KeystoreKeyProvider.java diff --git a/src/main/java/org/alfresco/encryption/MACUtils.java b/core/src/main/java/org/alfresco/encryption/MACUtils.java similarity index 100% rename from src/main/java/org/alfresco/encryption/MACUtils.java rename to core/src/main/java/org/alfresco/encryption/MACUtils.java diff --git a/src/main/java/org/alfresco/encryption/MissingKeyException.java b/core/src/main/java/org/alfresco/encryption/MissingKeyException.java similarity index 100% rename from src/main/java/org/alfresco/encryption/MissingKeyException.java rename to core/src/main/java/org/alfresco/encryption/MissingKeyException.java diff --git a/src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java b/core/src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java similarity index 100% rename from src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java rename to core/src/main/java/org/alfresco/encryption/SpringKeyResourceLoader.java diff --git a/src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java b/core/src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java similarity index 100% rename from src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java rename to core/src/main/java/org/alfresco/encryption/ssl/AuthSSLInitializationError.java diff --git a/src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java b/core/src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java similarity index 100% rename from src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java rename to core/src/main/java/org/alfresco/encryption/ssl/AuthSSLProtocolSocketFactory.java diff --git a/src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java b/core/src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java similarity index 100% rename from src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java rename to core/src/main/java/org/alfresco/encryption/ssl/SSLEncryptionParameters.java diff --git a/src/main/java/org/alfresco/error/AlfrescoRuntimeException.java b/core/src/main/java/org/alfresco/error/AlfrescoRuntimeException.java similarity index 100% rename from src/main/java/org/alfresco/error/AlfrescoRuntimeException.java rename to core/src/main/java/org/alfresco/error/AlfrescoRuntimeException.java diff --git a/src/main/java/org/alfresco/error/ExceptionStackUtil.java b/core/src/main/java/org/alfresco/error/ExceptionStackUtil.java similarity index 100% rename from src/main/java/org/alfresco/error/ExceptionStackUtil.java rename to core/src/main/java/org/alfresco/error/ExceptionStackUtil.java diff --git a/src/main/java/org/alfresco/error/StackTraceUtil.java b/core/src/main/java/org/alfresco/error/StackTraceUtil.java similarity index 100% rename from src/main/java/org/alfresco/error/StackTraceUtil.java rename to core/src/main/java/org/alfresco/error/StackTraceUtil.java diff --git a/src/main/java/org/alfresco/httpclient/AbstractHttpClient.java b/core/src/main/java/org/alfresco/httpclient/AbstractHttpClient.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/AbstractHttpClient.java rename to core/src/main/java/org/alfresco/httpclient/AbstractHttpClient.java diff --git a/src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java b/core/src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java rename to core/src/main/java/org/alfresco/httpclient/AlfrescoHttpClient.java diff --git a/src/main/java/org/alfresco/httpclient/AuthenticationException.java b/core/src/main/java/org/alfresco/httpclient/AuthenticationException.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/AuthenticationException.java rename to core/src/main/java/org/alfresco/httpclient/AuthenticationException.java diff --git a/src/main/java/org/alfresco/httpclient/GetRequest.java b/core/src/main/java/org/alfresco/httpclient/GetRequest.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/GetRequest.java rename to core/src/main/java/org/alfresco/httpclient/GetRequest.java diff --git a/src/main/java/org/alfresco/httpclient/HeadRequest.java b/core/src/main/java/org/alfresco/httpclient/HeadRequest.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/HeadRequest.java rename to core/src/main/java/org/alfresco/httpclient/HeadRequest.java diff --git a/src/main/java/org/alfresco/httpclient/HttpClientFactory.java b/core/src/main/java/org/alfresco/httpclient/HttpClientFactory.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/HttpClientFactory.java rename to core/src/main/java/org/alfresco/httpclient/HttpClientFactory.java diff --git a/src/main/java/org/alfresco/httpclient/HttpMethodResponse.java b/core/src/main/java/org/alfresco/httpclient/HttpMethodResponse.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/HttpMethodResponse.java rename to core/src/main/java/org/alfresco/httpclient/HttpMethodResponse.java diff --git a/src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java b/core/src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java rename to core/src/main/java/org/alfresco/httpclient/MD5EncryptionParameters.java diff --git a/src/main/java/org/alfresco/httpclient/PostRequest.java b/core/src/main/java/org/alfresco/httpclient/PostRequest.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/PostRequest.java rename to core/src/main/java/org/alfresco/httpclient/PostRequest.java diff --git a/src/main/java/org/alfresco/httpclient/Request.java b/core/src/main/java/org/alfresco/httpclient/Request.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/Request.java rename to core/src/main/java/org/alfresco/httpclient/Request.java diff --git a/src/main/java/org/alfresco/httpclient/Response.java b/core/src/main/java/org/alfresco/httpclient/Response.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/Response.java rename to core/src/main/java/org/alfresco/httpclient/Response.java diff --git a/src/main/java/org/alfresco/httpclient/SecureHttpClient.java b/core/src/main/java/org/alfresco/httpclient/SecureHttpClient.java similarity index 100% rename from src/main/java/org/alfresco/httpclient/SecureHttpClient.java rename to core/src/main/java/org/alfresco/httpclient/SecureHttpClient.java diff --git a/src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java b/core/src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java similarity index 100% rename from src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java rename to core/src/main/java/org/alfresco/i18n/ResourceBundleBootstrapComponent.java diff --git a/src/main/java/org/alfresco/processor/Processor.java b/core/src/main/java/org/alfresco/processor/Processor.java similarity index 100% rename from src/main/java/org/alfresco/processor/Processor.java rename to core/src/main/java/org/alfresco/processor/Processor.java diff --git a/src/main/java/org/alfresco/processor/ProcessorExtension.java b/core/src/main/java/org/alfresco/processor/ProcessorExtension.java similarity index 100% rename from src/main/java/org/alfresco/processor/ProcessorExtension.java rename to core/src/main/java/org/alfresco/processor/ProcessorExtension.java diff --git a/src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java b/core/src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java similarity index 100% rename from src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java rename to core/src/main/java/org/alfresco/query/AbstractCachingCannedQueryFactory.java diff --git a/src/main/java/org/alfresco/query/AbstractCannedQuery.java b/core/src/main/java/org/alfresco/query/AbstractCannedQuery.java similarity index 100% rename from src/main/java/org/alfresco/query/AbstractCannedQuery.java rename to core/src/main/java/org/alfresco/query/AbstractCannedQuery.java diff --git a/src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java b/core/src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java similarity index 100% rename from src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java rename to core/src/main/java/org/alfresco/query/AbstractCannedQueryFactory.java diff --git a/src/main/java/org/alfresco/query/CannedQuery.java b/core/src/main/java/org/alfresco/query/CannedQuery.java similarity index 100% rename from src/main/java/org/alfresco/query/CannedQuery.java rename to core/src/main/java/org/alfresco/query/CannedQuery.java diff --git a/src/main/java/org/alfresco/query/CannedQueryException.java b/core/src/main/java/org/alfresco/query/CannedQueryException.java similarity index 100% rename from src/main/java/org/alfresco/query/CannedQueryException.java rename to core/src/main/java/org/alfresco/query/CannedQueryException.java diff --git a/src/main/java/org/alfresco/query/CannedQueryFactory.java b/core/src/main/java/org/alfresco/query/CannedQueryFactory.java similarity index 100% rename from src/main/java/org/alfresco/query/CannedQueryFactory.java rename to core/src/main/java/org/alfresco/query/CannedQueryFactory.java diff --git a/src/main/java/org/alfresco/query/CannedQueryPageDetails.java b/core/src/main/java/org/alfresco/query/CannedQueryPageDetails.java similarity index 100% rename from src/main/java/org/alfresco/query/CannedQueryPageDetails.java rename to core/src/main/java/org/alfresco/query/CannedQueryPageDetails.java diff --git a/src/main/java/org/alfresco/query/CannedQueryParameters.java b/core/src/main/java/org/alfresco/query/CannedQueryParameters.java similarity index 100% rename from src/main/java/org/alfresco/query/CannedQueryParameters.java rename to core/src/main/java/org/alfresco/query/CannedQueryParameters.java diff --git a/src/main/java/org/alfresco/query/CannedQueryResults.java b/core/src/main/java/org/alfresco/query/CannedQueryResults.java similarity index 100% rename from src/main/java/org/alfresco/query/CannedQueryResults.java rename to core/src/main/java/org/alfresco/query/CannedQueryResults.java diff --git a/src/main/java/org/alfresco/query/CannedQuerySortDetails.java b/core/src/main/java/org/alfresco/query/CannedQuerySortDetails.java similarity index 100% rename from src/main/java/org/alfresco/query/CannedQuerySortDetails.java rename to core/src/main/java/org/alfresco/query/CannedQuerySortDetails.java diff --git a/src/main/java/org/alfresco/query/EmptyCannedQueryResults.java b/core/src/main/java/org/alfresco/query/EmptyCannedQueryResults.java similarity index 100% rename from src/main/java/org/alfresco/query/EmptyCannedQueryResults.java rename to core/src/main/java/org/alfresco/query/EmptyCannedQueryResults.java diff --git a/src/main/java/org/alfresco/query/EmptyPagingResults.java b/core/src/main/java/org/alfresco/query/EmptyPagingResults.java similarity index 100% rename from src/main/java/org/alfresco/query/EmptyPagingResults.java rename to core/src/main/java/org/alfresco/query/EmptyPagingResults.java diff --git a/src/main/java/org/alfresco/query/ListBackedPagingResults.java b/core/src/main/java/org/alfresco/query/ListBackedPagingResults.java similarity index 100% rename from src/main/java/org/alfresco/query/ListBackedPagingResults.java rename to core/src/main/java/org/alfresco/query/ListBackedPagingResults.java diff --git a/src/main/java/org/alfresco/query/PageDetails.java b/core/src/main/java/org/alfresco/query/PageDetails.java similarity index 100% rename from src/main/java/org/alfresco/query/PageDetails.java rename to core/src/main/java/org/alfresco/query/PageDetails.java diff --git a/src/main/java/org/alfresco/query/PagingRequest.java b/core/src/main/java/org/alfresco/query/PagingRequest.java similarity index 100% rename from src/main/java/org/alfresco/query/PagingRequest.java rename to core/src/main/java/org/alfresco/query/PagingRequest.java diff --git a/src/main/java/org/alfresco/query/PagingResults.java b/core/src/main/java/org/alfresco/query/PagingResults.java similarity index 100% rename from src/main/java/org/alfresco/query/PagingResults.java rename to core/src/main/java/org/alfresco/query/PagingResults.java diff --git a/src/main/java/org/alfresco/query/PermissionedResults.java b/core/src/main/java/org/alfresco/query/PermissionedResults.java similarity index 100% rename from src/main/java/org/alfresco/query/PermissionedResults.java rename to core/src/main/java/org/alfresco/query/PermissionedResults.java diff --git a/src/main/java/org/alfresco/scripts/ScriptException.java b/core/src/main/java/org/alfresco/scripts/ScriptException.java similarity index 100% rename from src/main/java/org/alfresco/scripts/ScriptException.java rename to core/src/main/java/org/alfresco/scripts/ScriptException.java diff --git a/src/main/java/org/alfresco/scripts/ScriptResourceHelper.java b/core/src/main/java/org/alfresco/scripts/ScriptResourceHelper.java similarity index 100% rename from src/main/java/org/alfresco/scripts/ScriptResourceHelper.java rename to core/src/main/java/org/alfresco/scripts/ScriptResourceHelper.java diff --git a/src/main/java/org/alfresco/scripts/ScriptResourceLoader.java b/core/src/main/java/org/alfresco/scripts/ScriptResourceLoader.java similarity index 100% rename from src/main/java/org/alfresco/scripts/ScriptResourceLoader.java rename to core/src/main/java/org/alfresco/scripts/ScriptResourceLoader.java diff --git a/src/main/java/org/alfresco/util/ArgumentHelper.java b/core/src/main/java/org/alfresco/util/ArgumentHelper.java similarity index 100% rename from src/main/java/org/alfresco/util/ArgumentHelper.java rename to core/src/main/java/org/alfresco/util/ArgumentHelper.java diff --git a/src/main/java/org/alfresco/util/BridgeTable.java b/core/src/main/java/org/alfresco/util/BridgeTable.java similarity index 100% rename from src/main/java/org/alfresco/util/BridgeTable.java rename to core/src/main/java/org/alfresco/util/BridgeTable.java diff --git a/src/main/java/org/alfresco/util/CachingDateFormat.java b/core/src/main/java/org/alfresco/util/CachingDateFormat.java similarity index 100% rename from src/main/java/org/alfresco/util/CachingDateFormat.java rename to core/src/main/java/org/alfresco/util/CachingDateFormat.java diff --git a/src/main/java/org/alfresco/util/Content.java b/core/src/main/java/org/alfresco/util/Content.java similarity index 100% rename from src/main/java/org/alfresco/util/Content.java rename to core/src/main/java/org/alfresco/util/Content.java diff --git a/src/main/java/org/alfresco/util/Convert.java b/core/src/main/java/org/alfresco/util/Convert.java similarity index 100% rename from src/main/java/org/alfresco/util/Convert.java rename to core/src/main/java/org/alfresco/util/Convert.java diff --git a/src/main/java/org/alfresco/util/Debug.java b/core/src/main/java/org/alfresco/util/Debug.java similarity index 100% rename from src/main/java/org/alfresco/util/Debug.java rename to core/src/main/java/org/alfresco/util/Debug.java diff --git a/src/main/java/org/alfresco/util/Deleter.java b/core/src/main/java/org/alfresco/util/Deleter.java similarity index 100% rename from src/main/java/org/alfresco/util/Deleter.java rename to core/src/main/java/org/alfresco/util/Deleter.java diff --git a/src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java b/core/src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java similarity index 100% rename from src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java rename to core/src/main/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java diff --git a/src/main/java/org/alfresco/util/EqualsHelper.java b/core/src/main/java/org/alfresco/util/EqualsHelper.java similarity index 100% rename from src/main/java/org/alfresco/util/EqualsHelper.java rename to core/src/main/java/org/alfresco/util/EqualsHelper.java diff --git a/src/main/java/org/alfresco/util/ExpiringValueCache.java b/core/src/main/java/org/alfresco/util/ExpiringValueCache.java similarity index 100% rename from src/main/java/org/alfresco/util/ExpiringValueCache.java rename to core/src/main/java/org/alfresco/util/ExpiringValueCache.java diff --git a/src/main/java/org/alfresco/util/FileFilterMode.java b/core/src/main/java/org/alfresco/util/FileFilterMode.java similarity index 100% rename from src/main/java/org/alfresco/util/FileFilterMode.java rename to core/src/main/java/org/alfresco/util/FileFilterMode.java diff --git a/src/main/java/org/alfresco/util/GUID.java b/core/src/main/java/org/alfresco/util/GUID.java similarity index 100% rename from src/main/java/org/alfresco/util/GUID.java rename to core/src/main/java/org/alfresco/util/GUID.java diff --git a/src/main/java/org/alfresco/util/IPUtils.java b/core/src/main/java/org/alfresco/util/IPUtils.java similarity index 100% rename from src/main/java/org/alfresco/util/IPUtils.java rename to core/src/main/java/org/alfresco/util/IPUtils.java diff --git a/src/main/java/org/alfresco/util/ISO8601DateFormat.java b/core/src/main/java/org/alfresco/util/ISO8601DateFormat.java similarity index 100% rename from src/main/java/org/alfresco/util/ISO8601DateFormat.java rename to core/src/main/java/org/alfresco/util/ISO8601DateFormat.java diff --git a/src/main/java/org/alfresco/util/InputStreamContent.java b/core/src/main/java/org/alfresco/util/InputStreamContent.java similarity index 100% rename from src/main/java/org/alfresco/util/InputStreamContent.java rename to core/src/main/java/org/alfresco/util/InputStreamContent.java diff --git a/src/main/java/org/alfresco/util/JMXUtils.java b/core/src/main/java/org/alfresco/util/JMXUtils.java similarity index 100% rename from src/main/java/org/alfresco/util/JMXUtils.java rename to core/src/main/java/org/alfresco/util/JMXUtils.java diff --git a/src/main/java/org/alfresco/util/LockHelper.java b/core/src/main/java/org/alfresco/util/LockHelper.java similarity index 100% rename from src/main/java/org/alfresco/util/LockHelper.java rename to core/src/main/java/org/alfresco/util/LockHelper.java diff --git a/src/main/java/org/alfresco/util/LogAdapter.java b/core/src/main/java/org/alfresco/util/LogAdapter.java similarity index 100% rename from src/main/java/org/alfresco/util/LogAdapter.java rename to core/src/main/java/org/alfresco/util/LogAdapter.java diff --git a/src/main/java/org/alfresco/util/LogTee.java b/core/src/main/java/org/alfresco/util/LogTee.java similarity index 100% rename from src/main/java/org/alfresco/util/LogTee.java rename to core/src/main/java/org/alfresco/util/LogTee.java diff --git a/src/main/java/org/alfresco/util/LogUtil.java b/core/src/main/java/org/alfresco/util/LogUtil.java similarity index 100% rename from src/main/java/org/alfresco/util/LogUtil.java rename to core/src/main/java/org/alfresco/util/LogUtil.java diff --git a/src/main/java/org/alfresco/util/MD5.java b/core/src/main/java/org/alfresco/util/MD5.java similarity index 100% rename from src/main/java/org/alfresco/util/MD5.java rename to core/src/main/java/org/alfresco/util/MD5.java diff --git a/src/main/java/org/alfresco/util/MaxSizeMap.java b/core/src/main/java/org/alfresco/util/MaxSizeMap.java similarity index 100% rename from src/main/java/org/alfresco/util/MaxSizeMap.java rename to core/src/main/java/org/alfresco/util/MaxSizeMap.java diff --git a/src/main/java/org/alfresco/util/OneToManyBiMap.java b/core/src/main/java/org/alfresco/util/OneToManyBiMap.java similarity index 100% rename from src/main/java/org/alfresco/util/OneToManyBiMap.java rename to core/src/main/java/org/alfresco/util/OneToManyBiMap.java diff --git a/src/main/java/org/alfresco/util/OneToManyHashBiMap.java b/core/src/main/java/org/alfresco/util/OneToManyHashBiMap.java similarity index 100% rename from src/main/java/org/alfresco/util/OneToManyHashBiMap.java rename to core/src/main/java/org/alfresco/util/OneToManyHashBiMap.java diff --git a/src/main/java/org/alfresco/util/OneToManyHashMap.java b/core/src/main/java/org/alfresco/util/OneToManyHashMap.java similarity index 100% rename from src/main/java/org/alfresco/util/OneToManyHashMap.java rename to core/src/main/java/org/alfresco/util/OneToManyHashMap.java diff --git a/src/main/java/org/alfresco/util/OneToManyMap.java b/core/src/main/java/org/alfresco/util/OneToManyMap.java similarity index 100% rename from src/main/java/org/alfresco/util/OneToManyMap.java rename to core/src/main/java/org/alfresco/util/OneToManyMap.java diff --git a/src/main/java/org/alfresco/util/PackageMarker.java b/core/src/main/java/org/alfresco/util/PackageMarker.java similarity index 100% rename from src/main/java/org/alfresco/util/PackageMarker.java rename to core/src/main/java/org/alfresco/util/PackageMarker.java diff --git a/src/main/java/org/alfresco/util/Pair.java b/core/src/main/java/org/alfresco/util/Pair.java similarity index 100% rename from src/main/java/org/alfresco/util/Pair.java rename to core/src/main/java/org/alfresco/util/Pair.java diff --git a/src/main/java/org/alfresco/util/ParameterCheck.java b/core/src/main/java/org/alfresco/util/ParameterCheck.java similarity index 100% rename from src/main/java/org/alfresco/util/ParameterCheck.java rename to core/src/main/java/org/alfresco/util/ParameterCheck.java diff --git a/src/main/java/org/alfresco/util/PathMapper.java b/core/src/main/java/org/alfresco/util/PathMapper.java similarity index 100% rename from src/main/java/org/alfresco/util/PathMapper.java rename to core/src/main/java/org/alfresco/util/PathMapper.java diff --git a/src/main/java/org/alfresco/util/PatternFilter.java b/core/src/main/java/org/alfresco/util/PatternFilter.java similarity index 100% rename from src/main/java/org/alfresco/util/PatternFilter.java rename to core/src/main/java/org/alfresco/util/PatternFilter.java diff --git a/src/main/java/org/alfresco/util/PropertyCheck.java b/core/src/main/java/org/alfresco/util/PropertyCheck.java similarity index 100% rename from src/main/java/org/alfresco/util/PropertyCheck.java rename to core/src/main/java/org/alfresco/util/PropertyCheck.java diff --git a/src/main/java/org/alfresco/util/ReadWriteLockExecuter.java b/core/src/main/java/org/alfresco/util/ReadWriteLockExecuter.java similarity index 100% rename from src/main/java/org/alfresco/util/ReadWriteLockExecuter.java rename to core/src/main/java/org/alfresco/util/ReadWriteLockExecuter.java diff --git a/src/main/java/org/alfresco/util/ReflectionHelper.java b/core/src/main/java/org/alfresco/util/ReflectionHelper.java similarity index 100% rename from src/main/java/org/alfresco/util/ReflectionHelper.java rename to core/src/main/java/org/alfresco/util/ReflectionHelper.java diff --git a/src/main/java/org/alfresco/util/SchedulerStarterBean.java b/core/src/main/java/org/alfresco/util/SchedulerStarterBean.java similarity index 100% rename from src/main/java/org/alfresco/util/SchedulerStarterBean.java rename to core/src/main/java/org/alfresco/util/SchedulerStarterBean.java diff --git a/src/main/java/org/alfresco/util/SerializationUtils.java b/core/src/main/java/org/alfresco/util/SerializationUtils.java similarity index 100% rename from src/main/java/org/alfresco/util/SerializationUtils.java rename to core/src/main/java/org/alfresco/util/SerializationUtils.java diff --git a/src/main/java/org/alfresco/util/TempFileProvider.java b/core/src/main/java/org/alfresco/util/TempFileProvider.java similarity index 100% rename from src/main/java/org/alfresco/util/TempFileProvider.java rename to core/src/main/java/org/alfresco/util/TempFileProvider.java diff --git a/src/main/java/org/alfresco/util/TraceableThreadFactory.java b/core/src/main/java/org/alfresco/util/TraceableThreadFactory.java similarity index 100% rename from src/main/java/org/alfresco/util/TraceableThreadFactory.java rename to core/src/main/java/org/alfresco/util/TraceableThreadFactory.java diff --git a/src/main/java/org/alfresco/util/TriggerBeanSPI.java b/core/src/main/java/org/alfresco/util/TriggerBeanSPI.java similarity index 100% rename from src/main/java/org/alfresco/util/TriggerBeanSPI.java rename to core/src/main/java/org/alfresco/util/TriggerBeanSPI.java diff --git a/src/main/java/org/alfresco/util/Triple.java b/core/src/main/java/org/alfresco/util/Triple.java similarity index 100% rename from src/main/java/org/alfresco/util/Triple.java rename to core/src/main/java/org/alfresco/util/Triple.java diff --git a/src/main/java/org/alfresco/util/VersionNumber.java b/core/src/main/java/org/alfresco/util/VersionNumber.java similarity index 100% rename from src/main/java/org/alfresco/util/VersionNumber.java rename to core/src/main/java/org/alfresco/util/VersionNumber.java diff --git a/src/main/java/org/alfresco/util/VmShutdownListener.java b/core/src/main/java/org/alfresco/util/VmShutdownListener.java similarity index 100% rename from src/main/java/org/alfresco/util/VmShutdownListener.java rename to core/src/main/java/org/alfresco/util/VmShutdownListener.java diff --git a/src/main/java/org/alfresco/util/bean/BooleanBean.java b/core/src/main/java/org/alfresco/util/bean/BooleanBean.java similarity index 100% rename from src/main/java/org/alfresco/util/bean/BooleanBean.java rename to core/src/main/java/org/alfresco/util/bean/BooleanBean.java diff --git a/src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java b/core/src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java similarity index 100% rename from src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java rename to core/src/main/java/org/alfresco/util/bean/HierarchicalBeanLoader.java diff --git a/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java b/core/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java rename to core/src/main/java/org/alfresco/util/cache/AbstractAsynchronouslyRefreshedCache.java diff --git a/src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java b/core/src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java rename to core/src/main/java/org/alfresco/util/cache/AbstractRefreshableCacheEvent.java diff --git a/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java b/core/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java rename to core/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCache.java diff --git a/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java b/core/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java rename to core/src/main/java/org/alfresco/util/cache/AsynchronouslyRefreshedCacheRegistry.java diff --git a/src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java b/core/src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java rename to core/src/main/java/org/alfresco/util/cache/DefaultAsynchronouslyRefreshedCacheRegistry.java diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCache.java b/core/src/main/java/org/alfresco/util/cache/RefreshableCache.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/RefreshableCache.java rename to core/src/main/java/org/alfresco/util/cache/RefreshableCache.java diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java b/core/src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java rename to core/src/main/java/org/alfresco/util/cache/RefreshableCacheEvent.java diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java b/core/src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java rename to core/src/main/java/org/alfresco/util/cache/RefreshableCacheListener.java diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java b/core/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java rename to core/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshEvent.java diff --git a/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java b/core/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java similarity index 100% rename from src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java rename to core/src/main/java/org/alfresco/util/cache/RefreshableCacheRefreshedEvent.java diff --git a/src/main/java/org/alfresco/util/collections/CollectionUtils.java b/core/src/main/java/org/alfresco/util/collections/CollectionUtils.java similarity index 100% rename from src/main/java/org/alfresco/util/collections/CollectionUtils.java rename to core/src/main/java/org/alfresco/util/collections/CollectionUtils.java diff --git a/src/main/java/org/alfresco/util/collections/EntryTransformer.java b/core/src/main/java/org/alfresco/util/collections/EntryTransformer.java similarity index 100% rename from src/main/java/org/alfresco/util/collections/EntryTransformer.java rename to core/src/main/java/org/alfresco/util/collections/EntryTransformer.java diff --git a/src/main/java/org/alfresco/util/collections/Filter.java b/core/src/main/java/org/alfresco/util/collections/Filter.java similarity index 100% rename from src/main/java/org/alfresco/util/collections/Filter.java rename to core/src/main/java/org/alfresco/util/collections/Filter.java diff --git a/src/main/java/org/alfresco/util/collections/Function.java b/core/src/main/java/org/alfresco/util/collections/Function.java similarity index 100% rename from src/main/java/org/alfresco/util/collections/Function.java rename to core/src/main/java/org/alfresco/util/collections/Function.java diff --git a/src/main/java/org/alfresco/util/collections/JsonUtils.java b/core/src/main/java/org/alfresco/util/collections/JsonUtils.java similarity index 100% rename from src/main/java/org/alfresco/util/collections/JsonUtils.java rename to core/src/main/java/org/alfresco/util/collections/JsonUtils.java diff --git a/src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java b/core/src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java similarity index 100% rename from src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java rename to core/src/main/java/org/alfresco/util/exec/ExecParameterTokenizer.java diff --git a/src/main/java/org/alfresco/util/exec/RuntimeExec.java b/core/src/main/java/org/alfresco/util/exec/RuntimeExec.java similarity index 100% rename from src/main/java/org/alfresco/util/exec/RuntimeExec.java rename to core/src/main/java/org/alfresco/util/exec/RuntimeExec.java diff --git a/src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java b/core/src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java similarity index 100% rename from src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java rename to core/src/main/java/org/alfresco/util/exec/RuntimeExecBootstrapBean.java diff --git a/src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java b/core/src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java similarity index 100% rename from src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java rename to core/src/main/java/org/alfresco/util/exec/RuntimeExecShutdownBean.java diff --git a/src/main/java/org/alfresco/util/log/NDC.java b/core/src/main/java/org/alfresco/util/log/NDC.java similarity index 100% rename from src/main/java/org/alfresco/util/log/NDC.java rename to core/src/main/java/org/alfresco/util/log/NDC.java diff --git a/src/main/java/org/alfresco/util/log/NDCDelegate.java b/core/src/main/java/org/alfresco/util/log/NDCDelegate.java similarity index 100% rename from src/main/java/org/alfresco/util/log/NDCDelegate.java rename to core/src/main/java/org/alfresco/util/log/NDCDelegate.java diff --git a/src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java b/core/src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java similarity index 100% rename from src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java rename to core/src/main/java/org/alfresco/util/log/log4j/Log4JNDC.java diff --git a/src/main/java/org/alfresco/util/random/NormalDistributionHelper.java b/core/src/main/java/org/alfresco/util/random/NormalDistributionHelper.java similarity index 100% rename from src/main/java/org/alfresco/util/random/NormalDistributionHelper.java rename to core/src/main/java/org/alfresco/util/random/NormalDistributionHelper.java diff --git a/src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java b/core/src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java similarity index 100% rename from src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java rename to core/src/main/java/org/alfresco/util/registry/NamedObjectRegistry.java diff --git a/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java b/core/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java similarity index 100% rename from src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java rename to core/src/main/java/org/alfresco/util/shard/ExplicitShardingPolicy.java diff --git a/src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java b/core/src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java similarity index 100% rename from src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java rename to core/src/main/java/org/alfresco/util/transaction/ConnectionPoolException.java diff --git a/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java b/core/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java similarity index 100% rename from src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java rename to core/src/main/java/org/alfresco/util/transaction/SpringAwareUserTransaction.java diff --git a/src/main/java/org/alfresco/util/transaction/TransactionListener.java b/core/src/main/java/org/alfresco/util/transaction/TransactionListener.java similarity index 100% rename from src/main/java/org/alfresco/util/transaction/TransactionListener.java rename to core/src/main/java/org/alfresco/util/transaction/TransactionListener.java diff --git a/src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java b/core/src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java similarity index 100% rename from src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java rename to core/src/main/java/org/alfresco/util/transaction/TransactionListenerAdapter.java diff --git a/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java b/core/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java similarity index 100% rename from src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java rename to core/src/main/java/org/alfresco/util/transaction/TransactionSupportUtil.java diff --git a/src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java b/core/src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java similarity index 100% rename from src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java rename to core/src/main/java/org/alfresco/web/scripts/servlet/StaticAssetCacheFilter.java diff --git a/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java b/core/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java similarity index 100% rename from src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java rename to core/src/main/java/org/alfresco/web/scripts/servlet/X509ServletFilterBase.java diff --git a/src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java b/core/src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java similarity index 100% rename from src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java rename to core/src/test/java/org/alfresco/config/SystemPropertiesSetterBeanTest.java diff --git a/src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java b/core/src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java similarity index 100% rename from src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java rename to core/src/test/java/org/alfresco/encryption/EncryptingOutputStreamTest.java diff --git a/src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java b/core/src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java similarity index 100% rename from src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java rename to core/src/test/java/org/alfresco/error/AlfrescoRuntimeExceptionTest.java diff --git a/src/test/java/org/alfresco/query/CannedQueryTest.java b/core/src/test/java/org/alfresco/query/CannedQueryTest.java similarity index 100% rename from src/test/java/org/alfresco/query/CannedQueryTest.java rename to core/src/test/java/org/alfresco/query/CannedQueryTest.java diff --git a/src/test/java/org/alfresco/util/BaseTest.java b/core/src/test/java/org/alfresco/util/BaseTest.java similarity index 100% rename from src/test/java/org/alfresco/util/BaseTest.java rename to core/src/test/java/org/alfresco/util/BaseTest.java diff --git a/src/test/java/org/alfresco/util/BridgeTableTest.java b/core/src/test/java/org/alfresco/util/BridgeTableTest.java similarity index 100% rename from src/test/java/org/alfresco/util/BridgeTableTest.java rename to core/src/test/java/org/alfresco/util/BridgeTableTest.java diff --git a/src/test/java/org/alfresco/util/CachingDateFormatTest.java b/core/src/test/java/org/alfresco/util/CachingDateFormatTest.java similarity index 100% rename from src/test/java/org/alfresco/util/CachingDateFormatTest.java rename to core/src/test/java/org/alfresco/util/CachingDateFormatTest.java diff --git a/src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java b/core/src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java similarity index 100% rename from src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java rename to core/src/test/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java diff --git a/src/test/java/org/alfresco/util/EqualsHelperTest.java b/core/src/test/java/org/alfresco/util/EqualsHelperTest.java similarity index 100% rename from src/test/java/org/alfresco/util/EqualsHelperTest.java rename to core/src/test/java/org/alfresco/util/EqualsHelperTest.java diff --git a/src/test/java/org/alfresco/util/GuidTest.java b/core/src/test/java/org/alfresco/util/GuidTest.java similarity index 100% rename from src/test/java/org/alfresco/util/GuidTest.java rename to core/src/test/java/org/alfresco/util/GuidTest.java diff --git a/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java b/core/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java similarity index 100% rename from src/test/java/org/alfresco/util/ISO8601DateFormatTest.java rename to core/src/test/java/org/alfresco/util/ISO8601DateFormatTest.java diff --git a/src/test/java/org/alfresco/util/LogAdapterTest.java b/core/src/test/java/org/alfresco/util/LogAdapterTest.java similarity index 100% rename from src/test/java/org/alfresco/util/LogAdapterTest.java rename to core/src/test/java/org/alfresco/util/LogAdapterTest.java diff --git a/src/test/java/org/alfresco/util/LogTeeTest.java b/core/src/test/java/org/alfresco/util/LogTeeTest.java similarity index 100% rename from src/test/java/org/alfresco/util/LogTeeTest.java rename to core/src/test/java/org/alfresco/util/LogTeeTest.java diff --git a/src/test/java/org/alfresco/util/PathMapperTest.java b/core/src/test/java/org/alfresco/util/PathMapperTest.java similarity index 100% rename from src/test/java/org/alfresco/util/PathMapperTest.java rename to core/src/test/java/org/alfresco/util/PathMapperTest.java diff --git a/src/test/java/org/alfresco/util/TempFileProviderTest.java b/core/src/test/java/org/alfresco/util/TempFileProviderTest.java similarity index 100% rename from src/test/java/org/alfresco/util/TempFileProviderTest.java rename to core/src/test/java/org/alfresco/util/TempFileProviderTest.java diff --git a/src/test/java/org/alfresco/util/VersionNumberTest.java b/core/src/test/java/org/alfresco/util/VersionNumberTest.java similarity index 100% rename from src/test/java/org/alfresco/util/VersionNumberTest.java rename to core/src/test/java/org/alfresco/util/VersionNumberTest.java diff --git a/src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java b/core/src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java similarity index 100% rename from src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java rename to core/src/test/java/org/alfresco/util/collections/CollectionUtilsTest.java diff --git a/src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java b/core/src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java similarity index 100% rename from src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java rename to core/src/test/java/org/alfresco/util/exec/ExecParameterTokenizerTest.java diff --git a/src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java b/core/src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java similarity index 100% rename from src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java rename to core/src/test/java/org/alfresco/util/exec/RuntimeExecBeansTest.java diff --git a/src/test/java/org/alfresco/util/exec/RuntimeExecTest.java b/core/src/test/java/org/alfresco/util/exec/RuntimeExecTest.java similarity index 100% rename from src/test/java/org/alfresco/util/exec/RuntimeExecTest.java rename to core/src/test/java/org/alfresco/util/exec/RuntimeExecTest.java diff --git a/src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java b/core/src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java similarity index 100% rename from src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java rename to core/src/test/java/org/alfresco/util/random/NormalDistributionHelperTest.java diff --git a/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java b/core/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java similarity index 100% rename from src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java rename to core/src/test/java/org/alfresco/util/shard/ExplicitShardingPolicyTest.java diff --git a/src/test/java/org/alfresco/util/testing/category/DBTests.java b/core/src/test/java/org/alfresco/util/testing/category/DBTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/DBTests.java rename to core/src/test/java/org/alfresco/util/testing/category/DBTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/DebugTests.java b/core/src/test/java/org/alfresco/util/testing/category/DebugTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/DebugTests.java rename to core/src/test/java/org/alfresco/util/testing/category/DebugTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/LuceneTests.java b/core/src/test/java/org/alfresco/util/testing/category/LuceneTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/LuceneTests.java rename to core/src/test/java/org/alfresco/util/testing/category/LuceneTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java b/core/src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java rename to core/src/test/java/org/alfresco/util/testing/category/NeverRunsTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/NonBuildTests.java b/core/src/test/java/org/alfresco/util/testing/category/NonBuildTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/NonBuildTests.java rename to core/src/test/java/org/alfresco/util/testing/category/NonBuildTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/PerformanceTests.java b/core/src/test/java/org/alfresco/util/testing/category/PerformanceTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/PerformanceTests.java rename to core/src/test/java/org/alfresco/util/testing/category/PerformanceTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/RedundantTests.java b/core/src/test/java/org/alfresco/util/testing/category/RedundantTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/RedundantTests.java rename to core/src/test/java/org/alfresco/util/testing/category/RedundantTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/SanityTests.java b/core/src/test/java/org/alfresco/util/testing/category/SanityTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/SanityTests.java rename to core/src/test/java/org/alfresco/util/testing/category/SanityTests.java diff --git a/src/test/java/org/alfresco/util/testing/category/SlowTests.java b/core/src/test/java/org/alfresco/util/testing/category/SlowTests.java similarity index 100% rename from src/test/java/org/alfresco/util/testing/category/SlowTests.java rename to core/src/test/java/org/alfresco/util/testing/category/SlowTests.java diff --git a/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java b/core/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java similarity index 100% rename from src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java rename to core/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java diff --git a/src/test/resources/config-areas.xml b/core/src/test/resources/config-areas.xml similarity index 100% rename from src/test/resources/config-areas.xml rename to core/src/test/resources/config-areas.xml diff --git a/src/test/resources/config-multi.xml b/core/src/test/resources/config-multi.xml similarity index 100% rename from src/test/resources/config-multi.xml rename to core/src/test/resources/config-multi.xml diff --git a/src/test/resources/config-props.properties b/core/src/test/resources/config-props.properties similarity index 100% rename from src/test/resources/config-props.properties rename to core/src/test/resources/config-props.properties diff --git a/src/test/resources/config-props.xml b/core/src/test/resources/config-props.xml similarity index 100% rename from src/test/resources/config-props.xml rename to core/src/test/resources/config-props.xml diff --git a/src/test/resources/config-replace.xml b/core/src/test/resources/config-replace.xml similarity index 100% rename from src/test/resources/config-replace.xml rename to core/src/test/resources/config-replace.xml diff --git a/src/test/resources/config.xml b/core/src/test/resources/config.xml similarity index 100% rename from src/test/resources/config.xml rename to core/src/test/resources/config.xml diff --git a/src/test/resources/org/alfresco/i18n/testMessages.properties b/core/src/test/resources/org/alfresco/i18n/testMessages.properties similarity index 100% rename from src/test/resources/org/alfresco/i18n/testMessages.properties rename to core/src/test/resources/org/alfresco/i18n/testMessages.properties diff --git a/src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties b/core/src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties similarity index 100% rename from src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties rename to core/src/test/resources/org/alfresco/i18n/testMessages_fr_FR.properties diff --git a/src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml b/core/src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml similarity index 100% rename from src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml rename to core/src/test/resources/org/alfresco/util/exec/RuntimeExecBeansTest-context.xml diff --git a/src/test/resources/resource-loader/java.util.AbstractCollection/file.txt b/core/src/test/resources/resource-loader/java.util.AbstractCollection/file.txt similarity index 100% rename from src/test/resources/resource-loader/java.util.AbstractCollection/file.txt rename to core/src/test/resources/resource-loader/java.util.AbstractCollection/file.txt diff --git a/src/test/resources/resource-loader/java.util.AbstractList/file.txt b/core/src/test/resources/resource-loader/java.util.AbstractList/file.txt similarity index 100% rename from src/test/resources/resource-loader/java.util.AbstractList/file.txt rename to core/src/test/resources/resource-loader/java.util.AbstractList/file.txt diff --git a/src/test/resources/resource-loader/java.util.TreeSet/file.txt b/core/src/test/resources/resource-loader/java.util.TreeSet/file.txt similarity index 100% rename from src/test/resources/resource-loader/java.util.TreeSet/file.txt rename to core/src/test/resources/resource-loader/java.util.TreeSet/file.txt diff --git a/src/test/resources/test-config-forms-basic-override.xml b/core/src/test/resources/test-config-forms-basic-override.xml similarity index 100% rename from src/test/resources/test-config-forms-basic-override.xml rename to core/src/test/resources/test-config-forms-basic-override.xml diff --git a/src/test/resources/test-config-forms-basic.xml b/core/src/test/resources/test-config-forms-basic.xml similarity index 100% rename from src/test/resources/test-config-forms-basic.xml rename to core/src/test/resources/test-config-forms-basic.xml diff --git a/src/test/resources/test-config-forms-negative.xml b/core/src/test/resources/test-config-forms-negative.xml similarity index 100% rename from src/test/resources/test-config-forms-negative.xml rename to core/src/test/resources/test-config-forms-negative.xml diff --git a/src/test/resources/test-config-forms.xml b/core/src/test/resources/test-config-forms.xml similarity index 100% rename from src/test/resources/test-config-forms.xml rename to core/src/test/resources/test-config-forms.xml

+ * This class is thread-safe in that it will detect multithreaded access and throw + * exceptions. Therefore do not use on multiple threads. Instances should be + * used only for the duration of the required user transaction and then discarded. + * Any attempt to reuse an instance will result in failure. + *